blob: a809cdf9072d5dd6958ea1ef5f42b0a494920080 [file] [log] [blame]
Mike Stroyana451fa82016-01-07 15:35:37 -07001#!/usr/bin/python3 -i
Dustin Gravesb8458d12016-03-28 16:17:38 -06002#
3# Copyright (c) 2013-2016 The Khronos Group Inc.
4#
5# Permission is hereby granted, free of charge, to any person obtaining a
6# copy of this software and/or associated documentation files (the
7# "Materials"), to deal in the Materials without restriction, including
8# without limitation the rights to use, copy, modify, merge, publish,
9# distribute, sublicense, and/or sell copies of the Materials, and to
10# permit persons to whom the Materials are furnished to do so, subject to
11# the following conditions:
12#
13# The above copyright notice and this permission notice shall be included
14# in all copies or substantial portions of the Materials.
15#
16# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22# MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
23
Mike Stroyana451fa82016-01-07 15:35:37 -070024import os,re,sys
Dustin Gravesf69e3772016-02-11 10:10:14 -070025from collections import namedtuple
Mike Stroyan0cf28a22016-04-05 16:40:30 -060026import xml.etree.ElementTree as etree
Mike Stroyana451fa82016-01-07 15:35:37 -070027
28def write( *args, **kwargs ):
29 file = kwargs.pop('file',sys.stdout)
30 end = kwargs.pop( 'end','\n')
31 file.write( ' '.join([str(arg) for arg in args]) )
32 file.write( end )
33
34# noneStr - returns string argument, or "" if argument is None.
Mike Stroyan0cf28a22016-04-05 16:40:30 -060035# Used in converting etree Elements into text.
Mike Stroyana451fa82016-01-07 15:35:37 -070036# str - string to convert
37def noneStr(str):
38 if (str):
39 return str
40 else:
41 return ""
42
43# enquote - returns string argument with surrounding quotes,
44# for serialization into Python code.
45def enquote(str):
46 if (str):
47 return "'" + str + "'"
48 else:
49 return None
50
51# Primary sort key for regSortFeatures.
52# Sorts by category of the feature name string:
53# Core API features (those defined with a <feature> tag)
54# ARB/KHR/OES (Khronos extensions)
55# other (EXT/vendor extensions)
56# This will need changing for Vulkan!
57def regSortCategoryKey(feature):
58 if (feature.elem.tag == 'feature'):
59 return 0
60 elif (feature.category == 'ARB' or
61 feature.category == 'KHR' or
62 feature.category == 'OES'):
63 return 1
64 else:
65 return 2
66
67# Secondary sort key for regSortFeatures.
68# Sorts by extension name.
69def regSortNameKey(feature):
70 return feature.name
71
72# Second sort key for regSortFeatures.
73# Sorts by feature version. <extension> elements all have version number "0"
74def regSortFeatureVersionKey(feature):
75 return float(feature.version)
76
77# Tertiary sort key for regSortFeatures.
78# Sorts by extension number. <feature> elements all have extension number 0.
79def regSortExtensionNumberKey(feature):
80 return int(feature.number)
81
82# regSortFeatures - default sort procedure for features.
83# Sorts by primary key of feature category ('feature' or 'extension')
84# then by version number (for features)
85# then by extension number (for extensions)
86def regSortFeatures(featureList):
87 featureList.sort(key = regSortExtensionNumberKey)
88 featureList.sort(key = regSortFeatureVersionKey)
89 featureList.sort(key = regSortCategoryKey)
90
91# GeneratorOptions - base class for options used during header production
92# These options are target language independent, and used by
93# Registry.apiGen() and by base OutputGenerator objects.
94#
95# Members
96# filename - name of file to generate, or None to write to stdout.
97# apiname - string matching <api> 'apiname' attribute, e.g. 'gl'.
98# profile - string specifying API profile , e.g. 'core', or None.
99# versions - regex matching API versions to process interfaces for.
100# Normally '.*' or '[0-9]\.[0-9]' to match all defined versions.
101# emitversions - regex matching API versions to actually emit
102# interfaces for (though all requested versions are considered
103# when deciding which interfaces to generate). For GL 4.3 glext.h,
104# this might be '1\.[2-5]|[2-4]\.[0-9]'.
105# defaultExtensions - If not None, a string which must in its
106# entirety match the pattern in the "supported" attribute of
107# the <extension>. Defaults to None. Usually the same as apiname.
108# addExtensions - regex matching names of additional extensions
109# to include. Defaults to None.
110# removeExtensions - regex matching names of extensions to
111# remove (after defaultExtensions and addExtensions). Defaults
112# to None.
113# sortProcedure - takes a list of FeatureInfo objects and sorts
114# them in place to a preferred order in the generated output.
115# Default is core API versions, ARB/KHR/OES extensions, all
116# other extensions, alphabetically within each group.
117# The regex patterns can be None or empty, in which case they match
118# nothing.
119class GeneratorOptions:
120 """Represents options during header production from an API registry"""
121 def __init__(self,
122 filename = None,
123 apiname = None,
124 profile = None,
125 versions = '.*',
126 emitversions = '.*',
127 defaultExtensions = None,
128 addExtensions = None,
129 removeExtensions = None,
130 sortProcedure = regSortFeatures):
131 self.filename = filename
132 self.apiname = apiname
133 self.profile = profile
134 self.versions = self.emptyRegex(versions)
135 self.emitversions = self.emptyRegex(emitversions)
136 self.defaultExtensions = defaultExtensions
137 self.addExtensions = self.emptyRegex(addExtensions)
138 self.removeExtensions = self.emptyRegex(removeExtensions)
139 self.sortProcedure = sortProcedure
140 #
141 # Substitute a regular expression which matches no version
142 # or extension names for None or the empty string.
143 def emptyRegex(self,pat):
144 if (pat == None or pat == ''):
145 return '_nomatch_^'
146 else:
147 return pat
148
149# CGeneratorOptions - subclass of GeneratorOptions.
150#
151# Adds options used by COutputGenerator objects during C language header
152# generation.
153#
154# Additional members
155# prefixText - list of strings to prefix generated header with
156# (usually a copyright statement + calling convention macros).
157# protectFile - True if multiple inclusion protection should be
158# generated (based on the filename) around the entire header.
159# protectFeature - True if #ifndef..#endif protection should be
160# generated around a feature interface in the header file.
161# genFuncPointers - True if function pointer typedefs should be
162# generated
163# protectProto - If conditional protection should be generated
164# around prototype declarations, set to either '#ifdef'
165# to require opt-in (#ifdef protectProtoStr) or '#ifndef'
166# to require opt-out (#ifndef protectProtoStr). Otherwise
167# set to None.
168# protectProtoStr - #ifdef/#ifndef symbol to use around prototype
169# declarations, if protectProto is set
170# apicall - string to use for the function declaration prefix,
171# such as APICALL on Windows.
172# apientry - string to use for the calling convention macro,
173# in typedefs, such as APIENTRY.
174# apientryp - string to use for the calling convention macro
175# in function pointer typedefs, such as APIENTRYP.
176# indentFuncProto - True if prototype declarations should put each
177# parameter on a separate line
178# indentFuncPointer - True if typedefed function pointers should put each
179# parameter on a separate line
180# alignFuncParam - if nonzero and parameters are being put on a
181# separate line, align parameter names at the specified column
182class CGeneratorOptions(GeneratorOptions):
183 """Represents options during C interface generation for headers"""
184 def __init__(self,
185 filename = None,
186 apiname = None,
187 profile = None,
188 versions = '.*',
189 emitversions = '.*',
190 defaultExtensions = None,
191 addExtensions = None,
192 removeExtensions = None,
193 sortProcedure = regSortFeatures,
194 prefixText = "",
195 genFuncPointers = True,
196 protectFile = True,
197 protectFeature = True,
198 protectProto = None,
199 protectProtoStr = None,
200 apicall = '',
201 apientry = '',
202 apientryp = '',
203 indentFuncProto = True,
204 indentFuncPointer = False,
205 alignFuncParam = 0):
206 GeneratorOptions.__init__(self, filename, apiname, profile,
207 versions, emitversions, defaultExtensions,
208 addExtensions, removeExtensions, sortProcedure)
209 self.prefixText = prefixText
210 self.genFuncPointers = genFuncPointers
211 self.protectFile = protectFile
212 self.protectFeature = protectFeature
213 self.protectProto = protectProto
214 self.protectProtoStr = protectProtoStr
215 self.apicall = apicall
216 self.apientry = apientry
217 self.apientryp = apientryp
218 self.indentFuncProto = indentFuncProto
219 self.indentFuncPointer = indentFuncPointer
220 self.alignFuncParam = alignFuncParam
221
222# DocGeneratorOptions - subclass of GeneratorOptions.
223#
224# Shares many members with CGeneratorOptions, since
225# both are writing C-style declarations:
226#
227# prefixText - list of strings to prefix generated header with
228# (usually a copyright statement + calling convention macros).
229# apicall - string to use for the function declaration prefix,
230# such as APICALL on Windows.
231# apientry - string to use for the calling convention macro,
232# in typedefs, such as APIENTRY.
233# apientryp - string to use for the calling convention macro
234# in function pointer typedefs, such as APIENTRYP.
235# genDirectory - directory into which to generate include files
236# indentFuncProto - True if prototype declarations should put each
237# parameter on a separate line
238# indentFuncPointer - True if typedefed function pointers should put each
239# parameter on a separate line
240# alignFuncParam - if nonzero and parameters are being put on a
241# separate line, align parameter names at the specified column
242#
243# Additional members:
244#
245class DocGeneratorOptions(GeneratorOptions):
246 """Represents options during C interface generation for Asciidoc"""
247 def __init__(self,
248 filename = None,
249 apiname = None,
250 profile = None,
251 versions = '.*',
252 emitversions = '.*',
253 defaultExtensions = None,
254 addExtensions = None,
255 removeExtensions = None,
256 sortProcedure = regSortFeatures,
257 prefixText = "",
258 apicall = '',
259 apientry = '',
260 apientryp = '',
261 genDirectory = 'gen',
262 indentFuncProto = True,
263 indentFuncPointer = False,
264 alignFuncParam = 0,
265 expandEnumerants = True):
266 GeneratorOptions.__init__(self, filename, apiname, profile,
267 versions, emitversions, defaultExtensions,
268 addExtensions, removeExtensions, sortProcedure)
269 self.prefixText = prefixText
270 self.apicall = apicall
271 self.apientry = apientry
272 self.apientryp = apientryp
273 self.genDirectory = genDirectory
274 self.indentFuncProto = indentFuncProto
275 self.indentFuncPointer = indentFuncPointer
276 self.alignFuncParam = alignFuncParam
277 self.expandEnumerants = expandEnumerants
278
Mike Stroyan8849f9a2015-11-02 15:30:20 -0700279# ThreadGeneratorOptions - subclass of GeneratorOptions.
280#
281# Adds options used by COutputGenerator objects during C language header
282# generation.
283#
284# Additional members
285# prefixText - list of strings to prefix generated header with
286# (usually a copyright statement + calling convention macros).
287# protectFile - True if multiple inclusion protection should be
288# generated (based on the filename) around the entire header.
289# protectFeature - True if #ifndef..#endif protection should be
290# generated around a feature interface in the header file.
291# genFuncPointers - True if function pointer typedefs should be
292# generated
293# protectProto - True if #ifdef..#endif protection should be
294# generated around prototype declarations
295# protectProtoStr - #ifdef symbol to use around prototype
296# declarations, if protected
297# apicall - string to use for the function declaration prefix,
298# such as APICALL on Windows.
299# apientry - string to use for the calling convention macro,
300# in typedefs, such as APIENTRY.
301# apientryp - string to use for the calling convention macro
302# in function pointer typedefs, such as APIENTRYP.
303# indentFuncProto - True if prototype declarations should put each
304# parameter on a separate line
305# indentFuncPointer - True if typedefed function pointers should put each
306# parameter on a separate line
307# alignFuncParam - if nonzero and parameters are being put on a
308# separate line, align parameter names at the specified column
309class ThreadGeneratorOptions(GeneratorOptions):
310 """Represents options during C interface generation for headers"""
311 def __init__(self,
312 filename = None,
313 apiname = None,
314 profile = None,
315 versions = '.*',
316 emitversions = '.*',
317 defaultExtensions = None,
318 addExtensions = None,
319 removeExtensions = None,
320 sortProcedure = regSortFeatures,
321 prefixText = "",
322 genFuncPointers = True,
323 protectFile = True,
324 protectFeature = True,
325 protectProto = True,
326 protectProtoStr = True,
327 apicall = '',
328 apientry = '',
329 apientryp = '',
330 indentFuncProto = True,
331 indentFuncPointer = False,
332 alignFuncParam = 0):
333 GeneratorOptions.__init__(self, filename, apiname, profile,
334 versions, emitversions, defaultExtensions,
335 addExtensions, removeExtensions, sortProcedure)
336 self.prefixText = prefixText
337 self.genFuncPointers = genFuncPointers
338 self.protectFile = protectFile
339 self.protectFeature = protectFeature
340 self.protectProto = protectProto
341 self.protectProtoStr = protectProtoStr
342 self.apicall = apicall
343 self.apientry = apientry
344 self.apientryp = apientryp
345 self.indentFuncProto = indentFuncProto
346 self.indentFuncPointer = indentFuncPointer
347 self.alignFuncParam = alignFuncParam
348
349
Dustin Gravesf69e3772016-02-11 10:10:14 -0700350# ParamCheckerGeneratorOptions - subclass of GeneratorOptions.
351#
Mark Lobodzinski1c333572016-03-17 15:08:18 -0600352# Adds options used by ParamCheckerOutputGenerator objects during parameter validation
Dustin Gravesf69e3772016-02-11 10:10:14 -0700353# generation.
354#
355# Additional members
356# prefixText - list of strings to prefix generated header with
357# (usually a copyright statement + calling convention macros).
358# protectFile - True if multiple inclusion protection should be
359# generated (based on the filename) around the entire header.
360# protectFeature - True if #ifndef..#endif protection should be
361# generated around a feature interface in the header file.
362# genFuncPointers - True if function pointer typedefs should be
363# generated
364# protectProto - If conditional protection should be generated
365# around prototype declarations, set to either '#ifdef'
366# to require opt-in (#ifdef protectProtoStr) or '#ifndef'
367# to require opt-out (#ifndef protectProtoStr). Otherwise
368# set to None.
369# protectProtoStr - #ifdef/#ifndef symbol to use around prototype
370# declarations, if protectProto is set
371# apicall - string to use for the function declaration prefix,
372# such as APICALL on Windows.
373# apientry - string to use for the calling convention macro,
374# in typedefs, such as APIENTRY.
375# apientryp - string to use for the calling convention macro
376# in function pointer typedefs, such as APIENTRYP.
377# indentFuncProto - True if prototype declarations should put each
378# parameter on a separate line
379# indentFuncPointer - True if typedefed function pointers should put each
380# parameter on a separate line
381# alignFuncParam - if nonzero and parameters are being put on a
382# separate line, align parameter names at the specified column
383class ParamCheckerGeneratorOptions(GeneratorOptions):
384 """Represents options during C interface generation for headers"""
385 def __init__(self,
386 filename = None,
387 apiname = None,
388 profile = None,
389 versions = '.*',
390 emitversions = '.*',
391 defaultExtensions = None,
392 addExtensions = None,
393 removeExtensions = None,
394 sortProcedure = regSortFeatures,
395 prefixText = "",
396 genFuncPointers = True,
397 protectFile = True,
398 protectFeature = True,
399 protectProto = None,
400 protectProtoStr = None,
401 apicall = '',
402 apientry = '',
403 apientryp = '',
404 indentFuncProto = True,
405 indentFuncPointer = False,
406 alignFuncParam = 0):
407 GeneratorOptions.__init__(self, filename, apiname, profile,
408 versions, emitversions, defaultExtensions,
409 addExtensions, removeExtensions, sortProcedure)
410 self.prefixText = prefixText
411 self.genFuncPointers = genFuncPointers
412 self.protectFile = protectFile
413 self.protectFeature = protectFeature
414 self.protectProto = protectProto
415 self.protectProtoStr = protectProtoStr
416 self.apicall = apicall
417 self.apientry = apientry
418 self.apientryp = apientryp
419 self.indentFuncProto = indentFuncProto
420 self.indentFuncPointer = indentFuncPointer
421 self.alignFuncParam = alignFuncParam
422
423
Mike Stroyana451fa82016-01-07 15:35:37 -0700424# OutputGenerator - base class for generating API interfaces.
425# Manages basic logic, logging, and output file control
426# Derived classes actually generate formatted output.
427#
428# ---- methods ----
429# OutputGenerator(errFile, warnFile, diagFile)
430# errFile, warnFile, diagFile - file handles to write errors,
431# warnings, diagnostics to. May be None to not write.
432# logMsg(level, *args) - log messages of different categories
433# level - 'error', 'warn', or 'diag'. 'error' will also
434# raise a UserWarning exception
435# *args - print()-style arguments
436# setExtMap(map) - specify a dictionary map from extension names to
437# numbers, used in creating values for extension enumerants.
438# beginFile(genOpts) - start a new interface file
439# genOpts - GeneratorOptions controlling what's generated and how
440# endFile() - finish an interface file, closing it when done
441# beginFeature(interface, emit) - write interface for a feature
442# and tag generated features as having been done.
443# interface - element for the <version> / <extension> to generate
444# emit - actually write to the header only when True
445# endFeature() - finish an interface.
446# genType(typeinfo,name) - generate interface for a type
447# typeinfo - TypeInfo for a type
448# genStruct(typeinfo,name) - generate interface for a C "struct" type.
449# typeinfo - TypeInfo for a type interpreted as a struct
450# genGroup(groupinfo,name) - generate interface for a group of enums (C "enum")
451# groupinfo - GroupInfo for a group
452# genEnum(enuminfo, name) - generate interface for an enum (constant)
453# enuminfo - EnumInfo for an enum
454# name - enum name
455# genCmd(cmdinfo) - generate interface for a command
456# cmdinfo - CmdInfo for a command
457# makeCDecls(cmd) - return C prototype and function pointer typedef for a
458# <command> Element, as a list of two strings
459# cmd - Element for the <command>
460# newline() - print a newline to the output file (utility function)
461#
462class OutputGenerator:
463 """Generate specified API interfaces in a specific style, such as a C header"""
464 def __init__(self,
465 errFile = sys.stderr,
466 warnFile = sys.stderr,
467 diagFile = sys.stdout):
468 self.outFile = None
469 self.errFile = errFile
470 self.warnFile = warnFile
471 self.diagFile = diagFile
472 # Internal state
473 self.featureName = None
474 self.genOpts = None
475 self.registry = None
476 # Used for extension enum value generation
477 self.extBase = 1000000000
478 self.extBlockSize = 1000
479 #
480 # logMsg - write a message of different categories to different
481 # destinations.
482 # level -
483 # 'diag' (diagnostic, voluminous)
484 # 'warn' (warning)
485 # 'error' (fatal error - raises exception after logging)
486 # *args - print()-style arguments to direct to corresponding log
487 def logMsg(self, level, *args):
488 """Log a message at the given level. Can be ignored or log to a file"""
489 if (level == 'error'):
490 strfile = io.StringIO()
491 write('ERROR:', *args, file=strfile)
492 if (self.errFile != None):
493 write(strfile.getvalue(), file=self.errFile)
494 raise UserWarning(strfile.getvalue())
495 elif (level == 'warn'):
496 if (self.warnFile != None):
497 write('WARNING:', *args, file=self.warnFile)
498 elif (level == 'diag'):
499 if (self.diagFile != None):
500 write('DIAG:', *args, file=self.diagFile)
501 else:
502 raise UserWarning(
503 '*** FATAL ERROR in Generator.logMsg: unknown level:' + level)
504 #
505 # enumToValue - parses and converts an <enum> tag into a value.
506 # Returns a list
507 # first element - integer representation of the value, or None
508 # if needsNum is False. The value must be a legal number
509 # if needsNum is True.
510 # second element - string representation of the value
511 # There are several possible representations of values.
512 # A 'value' attribute simply contains the value.
513 # A 'bitpos' attribute defines a value by specifying the bit
514 # position which is set in that value.
515 # A 'offset','extbase','extends' triplet specifies a value
516 # as an offset to a base value defined by the specified
517 # 'extbase' extension name, which is then cast to the
518 # typename specified by 'extends'. This requires probing
519 # the registry database, and imbeds knowledge of the
520 # Vulkan extension enum scheme in this function.
521 def enumToValue(self, elem, needsNum):
522 name = elem.get('name')
523 numVal = None
524 if ('value' in elem.keys()):
525 value = elem.get('value')
526 # print('About to translate value =', value, 'type =', type(value))
527 if (needsNum):
528 numVal = int(value, 0)
529 # If there's a non-integer, numeric 'type' attribute (e.g. 'u' or
530 # 'ull'), append it to the string value.
531 # t = enuminfo.elem.get('type')
532 # if (t != None and t != '' and t != 'i' and t != 's'):
533 # value += enuminfo.type
534 self.logMsg('diag', 'Enum', name, '-> value [', numVal, ',', value, ']')
535 return [numVal, value]
536 if ('bitpos' in elem.keys()):
537 value = elem.get('bitpos')
538 numVal = int(value, 0)
539 numVal = 1 << numVal
540 value = '0x%08x' % numVal
541 self.logMsg('diag', 'Enum', name, '-> bitpos [', numVal, ',', value, ']')
542 return [numVal, value]
543 if ('offset' in elem.keys()):
544 # Obtain values in the mapping from the attributes
545 enumNegative = False
546 offset = int(elem.get('offset'),0)
547 extnumber = int(elem.get('extnumber'),0)
548 extends = elem.get('extends')
549 if ('dir' in elem.keys()):
550 enumNegative = True
551 self.logMsg('diag', 'Enum', name, 'offset =', offset,
552 'extnumber =', extnumber, 'extends =', extends,
553 'enumNegative =', enumNegative)
554 # Now determine the actual enumerant value, as defined
555 # in the "Layers and Extensions" appendix of the spec.
556 numVal = self.extBase + (extnumber - 1) * self.extBlockSize + offset
557 if (enumNegative):
558 numVal = -numVal
559 value = '%d' % numVal
560 # More logic needed!
561 self.logMsg('diag', 'Enum', name, '-> offset [', numVal, ',', value, ']')
562 return [numVal, value]
563 return [None, None]
564 #
565 def beginFile(self, genOpts):
566 self.genOpts = genOpts
567 #
568 # Open specified output file. Not done in constructor since a
569 # Generator can be used without writing to a file.
570 if (self.genOpts.filename != None):
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.
Mike Stroyan0cf28a22016-04-05 16:40:30 -0600682 # etree has elem.text followed by (elem[i], elem[i].tail)
Mike Stroyana451fa82016-01-07 15:35:37 -0700683 # 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)
755class 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):
Dustin Gravesb8458d12016-03-28 16:17:38 -0600775 headerSym = re.sub('\.h', '_h_',
776 os.path.basename(self.genOpts.filename)).upper()
Mike Stroyana451fa82016-01-07 15:35:37 -0700777 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
Dustin Gravesb8458d12016-03-28 16:17:38 -0600915
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
Mike Stroyana451fa82016-01-07 15:35:37 -0700926 # Prefix
927 body = "\ntypedef enum " + groupName + " {\n"
928
Dustin Gravesb8458d12016-03-28 16:17:38 -0600929 isEnum = ('FLAG_BITS' not in expandPrefix)
930
Mike Stroyana451fa82016-01-07 15:35:37 -0700931 # 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')
Dustin Gravesb8458d12016-03-28 16:17:38 -0600942
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):
Mike Stroyana451fa82016-01-07 15:35:37 -0700951 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...
Dustin Gravesb8458d12016-03-28 16:17:38 -0600962 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
Mike Stroyana451fa82016-01-07 15:35:37 -0700969 # 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)
1014class 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
Dustin Gravesb8458d12016-03-28 16:17:38 -06001044 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
1045 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001046 write('[[{0},{0}]]'.format(basename), file=fp)
1047 write('["source","{basebackend@docbook:c++:cpp}",title=""]', file=fp)
Dustin Gravesb8458d12016-03-28 16:17:38 -06001048 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)
Mike Stroyana451fa82016-01-07 15:35:37 -07001052 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
Dustin Gravesb8458d12016-03-28 16:17:38 -06001114
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
Mike Stroyana451fa82016-01-07 15:35:37 -07001132 # 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')
Dustin Gravesb8458d12016-03-28 16:17:38 -06001144
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):
Mike Stroyana451fa82016-01-07 15:35:37 -07001153 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"
Dustin Gravesb8458d12016-03-28 16:17:38 -06001166 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"
Mike Stroyana451fa82016-01-07 15:35:37 -07001172 # 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)
1208class 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)
1343class 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
Dustin Gravesb8458d12016-03-28 16:17:38 -06001392 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001393
1394 # Valid Usage
1395 if validity is not None:
Dustin Gravesb8458d12016-03-28 16:17:38 -06001396 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001397 write('.Valid Usage', file=fp)
1398 write('*' * 80, file=fp)
Dustin Gravesb8458d12016-03-28 16:17:38 -06001399 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)
Mike Stroyana451fa82016-01-07 15:35:37 -07001404 write(validity, file=fp, end='')
Dustin Gravesb8458d12016-03-28 16:17:38 -06001405 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001406 write('*' * 80, file=fp)
Dustin Gravesb8458d12016-03-28 16:17:38 -06001407 write('endif::doctype-manpage[]', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001408 write('', file=fp)
1409
1410 # Host Synchronization
1411 if threadsafety is not None:
Dustin Gravesb8458d12016-03-28 16:17:38 -06001412 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001413 write('.Host Synchronization', file=fp)
1414 write('*' * 80, file=fp)
Dustin Gravesb8458d12016-03-28 16:17:38 -06001415 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)
Mike Stroyana451fa82016-01-07 15:35:37 -07001420 write(threadsafety, file=fp, end='')
Dustin Gravesb8458d12016-03-28 16:17:38 -06001421 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001422 write('*' * 80, file=fp)
Dustin Gravesb8458d12016-03-28 16:17:38 -06001423 write('endif::doctype-manpage[]', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001424 write('', file=fp)
Dustin Gravesf69e3772016-02-11 10:10:14 -07001425
Mike Stroyana451fa82016-01-07 15:35:37 -07001426 # Command Properties - contained within a block, to avoid table numbering
1427 if commandpropertiesentry is not None:
Dustin Gravesb8458d12016-03-28 16:17:38 -06001428 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001429 write('.Command Properties', file=fp)
1430 write('*' * 80, file=fp)
Dustin Gravesb8458d12016-03-28 16:17:38 -06001431 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)
Mike Stroyana451fa82016-01-07 15:35:37 -07001436 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)
Dustin Gravesb8458d12016-03-28 16:17:38 -06001441 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001442 write('*' * 80, file=fp)
Dustin Gravesb8458d12016-03-28 16:17:38 -06001443 write('endif::doctype-manpage[]', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001444 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:
Dustin Gravesb8458d12016-03-28 16:17:38 -06001448 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001449 write('.Return Codes', file=fp)
1450 write('*' * 80, file=fp)
Dustin Gravesb8458d12016-03-28 16:17:38 -06001451 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)
Mike Stroyana451fa82016-01-07 15:35:37 -07001456 if successcodes is not None:
Dustin Gravesb8458d12016-03-28 16:17:38 -06001457 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001458 write('<<fundamentals-successcodes,Success>>::', file=fp)
Dustin Gravesb8458d12016-03-28 16:17:38 -06001459 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)
Mike Stroyana451fa82016-01-07 15:35:37 -07001463 write(successcodes, file=fp)
1464 if errorcodes is not None:
Dustin Gravesb8458d12016-03-28 16:17:38 -06001465 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001466 write('<<fundamentals-errorcodes,Failure>>::', file=fp)
Dustin Gravesb8458d12016-03-28 16:17:38 -06001467 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)
Mike Stroyana451fa82016-01-07 15:35:37 -07001471 write(errorcodes, file=fp)
Dustin Gravesb8458d12016-03-28 16:17:38 -06001472 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001473 write('*' * 80, file=fp)
Dustin Gravesb8458d12016-03-28 16:17:38 -06001474 write('endif::doctype-manpage[]', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07001475 write('', file=fp)
Dustin Gravesf69e3772016-02-11 10:10:14 -07001476
Mike Stroyana451fa82016-01-07 15:35:37 -07001477 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 if validextensionstructs is None:
1903 asciidoc += ' must: be `NULL`'
1904 else:
1905 extensionstructs = validextensionstructs.split(',')
1906 asciidoc += ' must: point to one of ' + extensionstructs[:-1].join(', ') + ' or ' + extensionstructs[-1] + 'if the extension that introduced them is enabled '
1907
1908 asciidoc += '\n'
1909
1910 return asciidoc
1911
1912 #
1913 # Generate all the valid usage information for a given struct or command
1914 def makeValidUsageStatements(self, cmd, blockname, params, usages):
1915 # Start the asciidoc block for this
1916 asciidoc = ''
1917
1918 handles = []
1919 anyparentedhandlesoptional = False
1920 parentdictionary = {}
1921 arraylengths = set()
1922 for param in params:
1923 paramname = param.find('name')
1924 paramtype = param.find('type')
1925
1926 # Get the type's category
1927 typecategory = self.getTypeCategory(paramtype.text)
1928
1929 # Generate language to independently validate a parameter
1930 if paramtype.text == 'VkStructureType' and paramname.text == 'sType':
1931 asciidoc += self.makeStructureType(blockname, param)
1932 elif paramtype.text == 'void' and paramname.text == 'pNext':
1933 asciidoc += self.makeStructureExtensionPointer(param)
1934 else:
1935 asciidoc += self.createValidationLineForParameter(param, params, typecategory)
1936
1937 # Ensure that any parenting is properly validated, and list that a handle was found
1938 if typecategory == 'handle':
1939 # Don't detect a parent for return values!
1940 if not self.paramIsPointer(param) or (param.text is not None and 'const' in param.text):
1941 parent = self.getHandleParent(paramtype.text)
1942 if parent is not None:
1943 handles.append(param)
1944
1945 # If any param is optional, it affects the output
1946 if self.isHandleOptional(param, params):
1947 anyparentedhandlesoptional = True
1948
1949 # Find the first dispatchable parent
1950 ancestor = parent
1951 while ancestor is not None and not self.isHandleTypeDispatchable(ancestor):
1952 ancestor = self.getHandleParent(ancestor)
1953
1954 # If one was found, add this parameter to the parent dictionary
1955 if ancestor is not None:
1956 if ancestor not in parentdictionary:
1957 parentdictionary[ancestor] = []
1958
1959 if self.paramIsArray(param):
1960 parentdictionary[ancestor].append('the elements of ' + self.makeParameterName(paramname.text))
1961 else:
1962 parentdictionary[ancestor].append(self.makeParameterName(paramname.text))
1963
1964 # Get the array length for this parameter
1965 arraylength = param.attrib.get('len')
1966 if arraylength is not None:
1967 for onelength in arraylength.split(','):
1968 arraylengths.add(onelength)
1969
1970 # For any vkQueue* functions, there might be queue type data
1971 if 'vkQueue' in blockname:
1972 # The queue type must be valid
1973 queuetypes = cmd.attrib.get('queues')
1974 if queuetypes is not None:
1975 queuebits = []
1976 for queuetype in re.findall(r'([^,]+)', queuetypes):
1977 queuebits.append(queuetype.replace('_',' '))
1978
1979 asciidoc += '* '
1980 asciidoc += 'The pname:queue must: support '
1981 if len(queuebits) == 1:
1982 asciidoc += queuebits[0]
1983 else:
1984 asciidoc += (', ').join(queuebits[:-1])
1985 asciidoc += ' or '
1986 asciidoc += queuebits[-1]
1987 asciidoc += ' operations'
1988 asciidoc += '\n'
1989
1990 if 'vkCmd' in blockname:
1991 # The commandBuffer parameter must be being recorded
1992 asciidoc += '* '
1993 asciidoc += 'pname:commandBuffer must: be in the recording state'
1994 asciidoc += '\n'
1995
1996 # The queue type must be valid
1997 queuetypes = cmd.attrib.get('queues')
1998 queuebits = []
1999 for queuetype in re.findall(r'([^,]+)', queuetypes):
2000 queuebits.append(queuetype.replace('_',' '))
2001
2002 asciidoc += '* '
2003 asciidoc += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support '
2004 if len(queuebits) == 1:
2005 asciidoc += queuebits[0]
2006 else:
2007 asciidoc += (', ').join(queuebits[:-1])
2008 asciidoc += ' or '
2009 asciidoc += queuebits[-1]
2010 asciidoc += ' operations'
2011 asciidoc += '\n'
2012
2013 # Must be called inside/outside a renderpass appropriately
2014 renderpass = cmd.attrib.get('renderpass')
2015
2016 if renderpass != 'both':
2017 asciidoc += '* This command must: only be called '
2018 asciidoc += renderpass
2019 asciidoc += ' of a render pass instance'
2020 asciidoc += '\n'
2021
2022 # Must be in the right level command buffer
2023 cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
2024
2025 if cmdbufferlevel != 'primary,secondary':
2026 asciidoc += '* pname:commandBuffer must: be a '
2027 asciidoc += cmdbufferlevel
2028 asciidoc += ' sname:VkCommandBuffer'
2029 asciidoc += '\n'
2030
2031 # Any non-optional arraylengths should specify they must be greater than 0
2032 for param in params:
2033 paramname = param.find('name')
2034
2035 for arraylength in arraylengths:
2036 if paramname.text == arraylength and param.attrib.get('optional') is None:
2037 # Get all the array dependencies
2038 arrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
2039
2040 # Get all the optional array dependencies, including those not generating validity for some reason
2041 optionalarrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
2042 optionalarrays.extend(cmd.findall("param/[@len='" + arraylength + "'][@noautovalidity='true']"))
2043
2044 asciidoc += '* '
2045
2046 # Allow lengths to be arbitrary if all their dependents are optional
2047 if len(optionalarrays) == len(arrays) and len(optionalarrays) != 0:
2048 asciidoc += 'If '
2049 if len(optionalarrays) > 1:
2050 asciidoc += 'any of '
2051
2052 for array in optionalarrays[:-1]:
2053 asciidoc += self.makeParameterName(optionalarrays.find('name').text)
2054 asciidoc += ', '
2055
2056 if len(optionalarrays) > 1:
2057 asciidoc += 'and '
2058 asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
2059 asciidoc += ' are '
2060 else:
2061 asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
2062 asciidoc += ' is '
2063
2064 asciidoc += 'not `NULL`, '
2065
2066 if self.paramIsPointer(param):
2067 asciidoc += 'the value referenced by '
Mike Stroyana451fa82016-01-07 15:35:37 -07002068
2069 elif self.paramIsPointer(param):
2070 asciidoc += 'The value referenced by '
Mike Stroyana451fa82016-01-07 15:35:37 -07002071
2072 asciidoc += self.makeParameterName(arraylength)
2073 asciidoc += ' must: be greater than `0`'
2074 asciidoc += '\n'
2075
2076 # Find the parents of all objects referenced in this command
2077 for param in handles:
2078 asciidoc += self.makeAsciiDocHandleParent(param, params)
2079
2080 # Find the common ancestors of objects
2081 noancestorscount = 0
2082 while noancestorscount < len(parentdictionary):
2083 noancestorscount = 0
2084 oldparentdictionary = parentdictionary.copy()
2085 for parent in oldparentdictionary.items():
2086 ancestor = self.getHandleParent(parent[0])
2087
2088 while ancestor is not None and ancestor not in parentdictionary:
2089 ancestor = self.getHandleParent(ancestor)
2090
2091 if ancestor is not None:
2092 parentdictionary[ancestor] += parentdictionary.pop(parent[0])
2093 else:
2094 # No ancestors possible - so count it up
2095 noancestorscount += 1
2096
2097 # Add validation language about common ancestors
2098 for parent in parentdictionary.items():
2099 if len(parent[1]) > 1:
2100 parentlanguage = '* '
2101
2102 parentlanguage += 'Each of '
2103 parentlanguage += ", ".join(parent[1][:-1])
2104 parentlanguage += ' and '
2105 parentlanguage += parent[1][-1]
2106 if anyparentedhandlesoptional is True:
2107 parentlanguage += ' that are valid handles'
2108 parentlanguage += ' must: have been created, allocated or retrieved from the same '
2109 parentlanguage += self.makeStructName(parent[0])
2110 parentlanguage += '\n'
2111
2112 # Capitalize and add to the main language
2113 asciidoc += parentlanguage
2114
Dustin Gravesb8458d12016-03-28 16:17:38 -06002115 # Add in any plain-text validation language that should be added
Mike Stroyana451fa82016-01-07 15:35:37 -07002116 for usage in usages:
2117 asciidoc += '* '
Dustin Gravesb8458d12016-03-28 16:17:38 -06002118 asciidoc += usage
Mike Stroyana451fa82016-01-07 15:35:37 -07002119 asciidoc += '\n'
2120
2121 # In case there's nothing to report, return None
2122 if asciidoc == '':
2123 return None
2124 # Delimit the asciidoc block
2125 return asciidoc
2126
2127 def makeThreadSafetyBlock(self, cmd, paramtext):
2128 """Generate C function pointer typedef for <command> Element"""
2129 paramdecl = ''
2130
2131 # For any vkCmd* functions, the commandBuffer parameter must be being recorded
2132 if cmd.find('proto/name') is not None and 'vkCmd' in cmd.find('proto/name'):
2133 paramdecl += '* '
2134 paramdecl += 'The sname:VkCommandPool that pname:commandBuffer was created from'
2135 paramdecl += '\n'
2136
2137 # Find and add any parameters that are thread unsafe
2138 explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
2139 if (explicitexternsyncparams is not None):
2140 for param in explicitexternsyncparams:
2141 externsyncattribs = param.attrib.get('externsync')
2142 paramname = param.find('name')
2143 for externsyncattrib in externsyncattribs.split(','):
2144 paramdecl += '* '
2145 paramdecl += 'Host access to '
2146 if externsyncattrib == 'true':
2147 if self.paramIsArray(param):
2148 paramdecl += 'each member of ' + self.makeParameterName(paramname.text)
2149 elif self.paramIsPointer(param):
2150 paramdecl += 'the object referenced by ' + self.makeParameterName(paramname.text)
2151 else:
2152 paramdecl += self.makeParameterName(paramname.text)
2153 else:
2154 paramdecl += 'pname:'
2155 paramdecl += externsyncattrib
2156 paramdecl += ' must: be externally synchronized\n'
2157
2158 # Find and add any "implicit" parameters that are thread unsafe
2159 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2160 if (implicitexternsyncparams is not None):
2161 for elem in implicitexternsyncparams:
2162 paramdecl += '* '
2163 paramdecl += 'Host access to '
2164 paramdecl += elem.text
2165 paramdecl += ' must: be externally synchronized\n'
2166
2167 if (paramdecl == ''):
2168 return None
2169 else:
2170 return paramdecl
2171
2172 def makeCommandPropertiesTableEntry(self, cmd, name):
Dustin Gravesf69e3772016-02-11 10:10:14 -07002173
Mike Stroyana451fa82016-01-07 15:35:37 -07002174 if 'vkCmd' in name:
2175 # Must be called inside/outside a renderpass appropriately
2176 cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
2177 cmdbufferlevel = (' + \n').join(cmdbufferlevel.title().split(','))
Dustin Gravesf69e3772016-02-11 10:10:14 -07002178
Mike Stroyana451fa82016-01-07 15:35:37 -07002179 renderpass = cmd.attrib.get('renderpass')
2180 renderpass = renderpass.capitalize()
Dustin Gravesf69e3772016-02-11 10:10:14 -07002181
Mike Stroyana451fa82016-01-07 15:35:37 -07002182 queues = cmd.attrib.get('queues')
2183 queues = (' + \n').join(queues.upper().split(','))
Dustin Gravesf69e3772016-02-11 10:10:14 -07002184
2185 return '|' + cmdbufferlevel + '|' + renderpass + '|' + queues
Mike Stroyana451fa82016-01-07 15:35:37 -07002186 elif 'vkQueue' in name:
2187 # Must be called inside/outside a renderpass appropriately
Dustin Gravesf69e3772016-02-11 10:10:14 -07002188
Mike Stroyana451fa82016-01-07 15:35:37 -07002189 queues = cmd.attrib.get('queues')
2190 if queues is None:
2191 queues = 'Any'
2192 else:
2193 queues = (' + \n').join(queues.upper().split(','))
Dustin Gravesf69e3772016-02-11 10:10:14 -07002194
2195 return '|-|-|' + queues
Mike Stroyana451fa82016-01-07 15:35:37 -07002196
2197 return None
Dustin Gravesf69e3772016-02-11 10:10:14 -07002198
Mike Stroyana451fa82016-01-07 15:35:37 -07002199 def makeSuccessCodes(self, cmd, name):
2200
2201 successcodes = cmd.attrib.get('successcodes')
2202 if successcodes is not None:
Dustin Gravesf69e3772016-02-11 10:10:14 -07002203
Mike Stroyana451fa82016-01-07 15:35:37 -07002204 successcodeentry = ''
2205 successcodes = successcodes.split(',')
Dustin Gravesb8458d12016-03-28 16:17:38 -06002206 return '* ename:' + '\n* ename:'.join(successcodes)
Mike Stroyana451fa82016-01-07 15:35:37 -07002207
2208 return None
2209
2210 def makeErrorCodes(self, cmd, name):
2211
2212 errorcodes = cmd.attrib.get('errorcodes')
2213 if errorcodes is not None:
Dustin Gravesf69e3772016-02-11 10:10:14 -07002214
Mike Stroyana451fa82016-01-07 15:35:37 -07002215 errorcodeentry = ''
2216 errorcodes = errorcodes.split(',')
Dustin Gravesb8458d12016-03-28 16:17:38 -06002217 return '* ename:' + '\n* ename:'.join(errorcodes)
Mike Stroyana451fa82016-01-07 15:35:37 -07002218
2219 return None
2220
2221 #
2222 # Command generation
2223 def genCmd(self, cmdinfo, name):
2224 OutputGenerator.genCmd(self, cmdinfo, name)
2225 #
Dustin Gravesb8458d12016-03-28 16:17:38 -06002226 # Get all the parameters
Mike Stroyana451fa82016-01-07 15:35:37 -07002227 params = cmdinfo.elem.findall('param')
Dustin Gravesb8458d12016-03-28 16:17:38 -06002228 usageelements = cmdinfo.elem.findall('validity/usage')
2229 usages = []
2230
2231 for usage in usageelements:
2232 usages.append(usage.text)
2233 for usage in cmdinfo.additionalValidity:
2234 usages.append(usage.text)
2235 for usage in cmdinfo.removedValidity:
2236 usages.remove(usage.text)
Mike Stroyana451fa82016-01-07 15:35:37 -07002237
2238 validity = self.makeValidUsageStatements(cmdinfo.elem, name, params, usages)
2239 threadsafety = self.makeThreadSafetyBlock(cmdinfo.elem, 'param')
2240 commandpropertiesentry = self.makeCommandPropertiesTableEntry(cmdinfo.elem, name)
2241 successcodes = self.makeSuccessCodes(cmdinfo.elem, name)
2242 errorcodes = self.makeErrorCodes(cmdinfo.elem, name)
2243
2244 self.writeInclude('validity/protos', name, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes)
2245
2246 #
2247 # Struct Generation
2248 def genStruct(self, typeinfo, typename):
2249 OutputGenerator.genStruct(self, typeinfo, typename)
2250
2251 # Anything that's only ever returned can't be set by the user, so shouldn't have any validity information.
2252 if typeinfo.elem.attrib.get('returnedonly') is None:
2253 params = typeinfo.elem.findall('member')
Dustin Gravesb8458d12016-03-28 16:17:38 -06002254
2255 usageelements = typeinfo.elem.findall('validity/usage')
2256 usages = []
2257
2258 for usage in usageelements:
2259 usages.append(usage.text)
2260 for usage in typeinfo.additionalValidity:
2261 usages.append(usage.text)
2262 for usage in typeinfo.removedValidity:
2263 usages.remove(usage.text)
Mike Stroyana451fa82016-01-07 15:35:37 -07002264
2265 validity = self.makeValidUsageStatements(typeinfo.elem, typename, params, usages)
2266 threadsafety = self.makeThreadSafetyBlock(typeinfo.elem, 'member')
2267
2268 self.writeInclude('validity/structs', typename, validity, threadsafety, None, None, None)
2269 else:
2270 # Still generate files for return only structs, in case this state changes later
2271 self.writeInclude('validity/structs', typename, None, None, None, None, None)
2272
2273 #
2274 # Type Generation
2275 def genType(self, typeinfo, typename):
2276 OutputGenerator.genType(self, typeinfo, typename)
2277
2278 category = typeinfo.elem.get('category')
2279 if (category == 'struct' or category == 'union'):
2280 self.genStruct(typeinfo, typename)
2281
2282# HostSynchronizationOutputGenerator - subclass of OutputGenerator.
2283# Generates AsciiDoc includes of the externsync parameter table for the
2284# fundamentals chapter of the Vulkan specification. Similar to
2285# DocOutputGenerator.
2286#
2287# ---- methods ----
2288# HostSynchronizationOutputGenerator(errFile, warnFile, diagFile) - args as for
2289# OutputGenerator. Defines additional internal state.
2290# ---- methods overriding base class ----
2291# genCmd(cmdinfo)
2292class HostSynchronizationOutputGenerator(OutputGenerator):
2293 # Generate Host Synchronized Parameters in a table at the top of the spec
2294 def __init__(self,
2295 errFile = sys.stderr,
2296 warnFile = sys.stderr,
2297 diagFile = sys.stdout):
2298 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2299
2300 threadsafety = {'parameters': '', 'parameterlists': '', 'implicit': ''}
2301
2302 def makeParameterName(self, name):
2303 return 'pname:' + name
2304
2305 def makeFLink(self, name):
2306 return 'flink:' + name
2307
2308 #
2309 # Generate an include file
2310 #
2311 # directory - subdirectory to put file in
2312 # basename - base name of the file
2313 # contents - contents of the file (Asciidoc boilerplate aside)
2314 def writeInclude(self):
2315
2316 if self.threadsafety['parameters'] is not None:
2317 # Create file
2318 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameters.txt'
2319 self.logMsg('diag', '# Generating include file:', filename)
2320 fp = open(filename, 'w')
2321
2322 # Host Synchronization
Dustin Gravesb8458d12016-03-28 16:17:38 -06002323 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07002324 write('.Externally Synchronized Parameters', file=fp)
2325 write('*' * 80, file=fp)
2326 write(self.threadsafety['parameters'], file=fp, end='')
2327 write('*' * 80, file=fp)
2328 write('', file=fp)
2329
2330 if self.threadsafety['parameterlists'] is not None:
2331 # Create file
2332 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameterlists.txt'
2333 self.logMsg('diag', '# Generating include file:', filename)
2334 fp = open(filename, 'w')
2335
2336 # Host Synchronization
Dustin Gravesb8458d12016-03-28 16:17:38 -06002337 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07002338 write('.Externally Synchronized Parameter Lists', file=fp)
2339 write('*' * 80, file=fp)
2340 write(self.threadsafety['parameterlists'], file=fp, end='')
2341 write('*' * 80, file=fp)
2342 write('', file=fp)
2343
2344 if self.threadsafety['implicit'] is not None:
2345 # Create file
2346 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/implicit.txt'
2347 self.logMsg('diag', '# Generating include file:', filename)
2348 fp = open(filename, 'w')
2349
2350 # Host Synchronization
Dustin Gravesb8458d12016-03-28 16:17:38 -06002351 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyana451fa82016-01-07 15:35:37 -07002352 write('.Implicit Externally Synchronized Parameters', file=fp)
2353 write('*' * 80, file=fp)
2354 write(self.threadsafety['implicit'], file=fp, end='')
2355 write('*' * 80, file=fp)
2356 write('', file=fp)
2357
2358 fp.close()
2359
2360 #
2361 # Check if the parameter passed in is a pointer to an array
2362 def paramIsArray(self, param):
2363 return param.attrib.get('len') is not None
2364
2365 # Check if the parameter passed in is a pointer
2366 def paramIsPointer(self, param):
2367 ispointer = False
2368 paramtype = param.find('type')
2369 if paramtype.tail is not None and '*' in paramtype.tail:
2370 ispointer = True
2371
2372 return ispointer
2373
2374 # Turn the "name[].member[]" notation into plain English.
2375 def makeThreadDereferenceHumanReadable(self, dereference):
2376 matches = re.findall(r"[\w]+[^\w]*",dereference)
2377 stringval = ''
2378 for match in reversed(matches):
2379 if '->' in match or '.' in match:
2380 stringval += 'member of '
2381 if '[]' in match:
2382 stringval += 'each element of '
2383
2384 stringval += 'the '
2385 stringval += self.makeParameterName(re.findall(r"[\w]+",match)[0])
2386 stringval += ' '
2387
2388 stringval += 'parameter'
2389
2390 return stringval[0].upper() + stringval[1:]
2391
2392 def makeThreadSafetyBlocks(self, cmd, paramtext):
2393 protoname = cmd.find('proto/name').text
2394
2395 # Find and add any parameters that are thread unsafe
2396 explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
2397 if (explicitexternsyncparams is not None):
2398 for param in explicitexternsyncparams:
2399 externsyncattribs = param.attrib.get('externsync')
2400 paramname = param.find('name')
2401 for externsyncattrib in externsyncattribs.split(','):
2402
2403 tempstring = '* '
2404 if externsyncattrib == 'true':
2405 if self.paramIsArray(param):
2406 tempstring += 'Each element of the '
2407 elif self.paramIsPointer(param):
2408 tempstring += 'The object referenced by the '
2409 else:
2410 tempstring += 'The '
2411
2412 tempstring += self.makeParameterName(paramname.text)
2413 tempstring += ' parameter'
2414
2415 else:
2416 tempstring += self.makeThreadDereferenceHumanReadable(externsyncattrib)
2417
2418 tempstring += ' in '
2419 tempstring += self.makeFLink(protoname)
2420 tempstring += '\n'
2421
2422
2423 if ' element of ' in tempstring:
2424 self.threadsafety['parameterlists'] += tempstring
2425 else:
2426 self.threadsafety['parameters'] += tempstring
2427
2428
2429 # Find and add any "implicit" parameters that are thread unsafe
2430 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2431 if (implicitexternsyncparams is not None):
2432 for elem in implicitexternsyncparams:
2433 self.threadsafety['implicit'] += '* '
2434 self.threadsafety['implicit'] += elem.text[0].upper()
2435 self.threadsafety['implicit'] += elem.text[1:]
2436 self.threadsafety['implicit'] += ' in '
2437 self.threadsafety['implicit'] += self.makeFLink(protoname)
2438 self.threadsafety['implicit'] += '\n'
2439
2440
2441 # For any vkCmd* functions, the commandBuffer parameter must be being recorded
2442 if protoname is not None and 'vkCmd' in protoname:
2443 self.threadsafety['implicit'] += '* '
2444 self.threadsafety['implicit'] += 'The sname:VkCommandPool that pname:commandBuffer was allocated from, in '
2445 self.threadsafety['implicit'] += self.makeFLink(protoname)
2446
2447 self.threadsafety['implicit'] += '\n'
2448
2449 #
2450 # Command generation
2451 def genCmd(self, cmdinfo, name):
2452 OutputGenerator.genCmd(self, cmdinfo, name)
2453 #
2454 # Get all thh parameters
2455 params = cmdinfo.elem.findall('param')
2456 usages = cmdinfo.elem.findall('validity/usage')
2457
2458 self.makeThreadSafetyBlocks(cmdinfo.elem, 'param')
2459
2460 self.writeInclude()
Mike Stroyan8849f9a2015-11-02 15:30:20 -07002461
2462# ThreadOutputGenerator - subclass of OutputGenerator.
2463# Generates Thread checking framework
2464#
2465# ---- methods ----
2466# ThreadOutputGenerator(errFile, warnFile, diagFile) - args as for
2467# OutputGenerator. Defines additional internal state.
2468# ---- methods overriding base class ----
2469# beginFile(genOpts)
2470# endFile()
2471# beginFeature(interface, emit)
2472# endFeature()
2473# genType(typeinfo,name)
2474# genStruct(typeinfo,name)
2475# genGroup(groupinfo,name)
2476# genEnum(enuminfo, name)
2477# genCmd(cmdinfo)
2478class ThreadOutputGenerator(OutputGenerator):
2479 """Generate specified API interfaces in a specific style, such as a C header"""
2480 # This is an ordered list of sections in the header file.
2481 TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
2482 'group', 'bitmask', 'funcpointer', 'struct']
2483 ALL_SECTIONS = TYPE_SECTIONS + ['command']
2484 def __init__(self,
2485 errFile = sys.stderr,
2486 warnFile = sys.stderr,
2487 diagFile = sys.stdout):
2488 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2489 # Internal state - accumulators for different inner block text
2490 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2491 self.intercepts = []
2492
2493 # Check if the parameter passed in is a pointer to an array
2494 def paramIsArray(self, param):
2495 return param.attrib.get('len') is not None
2496
2497 # Check if the parameter passed in is a pointer
2498 def paramIsPointer(self, param):
2499 ispointer = False
2500 for elem in param:
2501 #write('paramIsPointer '+elem.text, file=sys.stderr)
2502 #write('elem.tag '+elem.tag, file=sys.stderr)
2503 #if (elem.tail is None):
2504 # write('elem.tail is None', file=sys.stderr)
2505 #else:
2506 # write('elem.tail '+elem.tail, file=sys.stderr)
2507 if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
2508 ispointer = True
2509 # write('is pointer', file=sys.stderr)
2510 return ispointer
2511 def makeThreadUseBlock(self, cmd, functionprefix):
2512 """Generate C function pointer typedef for <command> Element"""
2513 paramdecl = ''
2514 thread_check_dispatchable_objects = [
2515 "VkCommandBuffer",
2516 "VkDevice",
2517 "VkInstance",
2518 "VkQueue",
2519 ]
2520 thread_check_nondispatchable_objects = [
2521 "VkBuffer",
2522 "VkBufferView",
2523 "VkCommandPool",
2524 "VkDescriptorPool",
2525 "VkDescriptorSetLayout",
2526 "VkDeviceMemory",
2527 "VkEvent",
2528 "VkFence",
2529 "VkFramebuffer",
2530 "VkImage",
2531 "VkImageView",
2532 "VkPipeline",
2533 "VkPipelineCache",
2534 "VkPipelineLayout",
2535 "VkQueryPool",
2536 "VkRenderPass",
2537 "VkSampler",
2538 "VkSemaphore",
2539 "VkShaderModule",
2540 ]
2541
2542 # Find and add any parameters that are thread unsafe
2543 params = cmd.findall('param')
2544 for param in params:
2545 paramname = param.find('name')
2546 if False: # self.paramIsPointer(param):
2547 paramdecl += ' // not watching use of pointer ' + paramname.text + '\n'
2548 else:
2549 externsync = param.attrib.get('externsync')
2550 if externsync == 'true':
2551 if self.paramIsArray(param):
Michael Mc Donnell3e04b612016-03-17 21:18:32 -07002552 paramdecl += ' for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
Mike Stroyan8849f9a2015-11-02 15:30:20 -07002553 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + '[index]);\n'
2554 paramdecl += ' }\n'
2555 else:
2556 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + ');\n'
2557 elif (param.attrib.get('externsync')):
2558 if self.paramIsArray(param):
2559 # Externsync can list pointers to arrays of members to synchronize
Michael Mc Donnell3e04b612016-03-17 21:18:32 -07002560 paramdecl += ' for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
Mike Stroyan8849f9a2015-11-02 15:30:20 -07002561 for member in externsync.split(","):
2562 # Replace first empty [] in member name with index
2563 element = member.replace('[]','[index]',1)
2564 if '[]' in element:
2565 # Replace any second empty [] in element name with
2566 # inner array index based on mapping array names like
2567 # "pSomeThings[]" to "someThingCount" array size.
2568 # This could be more robust by mapping a param member
2569 # name to a struct type and "len" attribute.
2570 limit = element[0:element.find('s[]')] + 'Count'
2571 dotp = limit.rfind('.p')
2572 limit = limit[0:dotp+1] + limit[dotp+2:dotp+3].lower() + limit[dotp+3:]
Michael Mc Donnell3e04b612016-03-17 21:18:32 -07002573 paramdecl += ' for(uint32_t index2=0;index2<'+limit+';index2++)'
Mike Stroyan8849f9a2015-11-02 15:30:20 -07002574 element = element.replace('[]','[index2]')
2575 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + element + ');\n'
2576 paramdecl += ' }\n'
2577 else:
2578 # externsync can list members to synchronize
2579 for member in externsync.split(","):
2580 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + member + ');\n'
2581 else:
2582 paramtype = param.find('type')
2583 if paramtype is not None:
2584 paramtype = paramtype.text
2585 else:
2586 paramtype = 'None'
2587 if paramtype in thread_check_dispatchable_objects or paramtype in thread_check_nondispatchable_objects:
2588 if self.paramIsArray(param) and ('pPipelines' != paramname.text):
Michael Mc Donnell3e04b612016-03-17 21:18:32 -07002589 paramdecl += ' for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
Mike Stroyan8849f9a2015-11-02 15:30:20 -07002590 paramdecl += ' ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + '[index]);\n'
2591 paramdecl += ' }\n'
2592 elif not self.paramIsPointer(param):
2593 # Pointer params are often being created.
2594 # They are not being read from.
2595 paramdecl += ' ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + ');\n'
2596 explicitexternsyncparams = cmd.findall("param[@externsync]")
2597 if (explicitexternsyncparams is not None):
2598 for param in explicitexternsyncparams:
2599 externsyncattrib = param.attrib.get('externsync')
2600 paramname = param.find('name')
2601 paramdecl += '// Host access to '
2602 if externsyncattrib == 'true':
2603 if self.paramIsArray(param):
2604 paramdecl += 'each member of ' + paramname.text
2605 elif self.paramIsPointer(param):
2606 paramdecl += 'the object referenced by ' + paramname.text
2607 else:
2608 paramdecl += paramname.text
2609 else:
2610 paramdecl += externsyncattrib
2611 paramdecl += ' must be externally synchronized\n'
2612
2613 # Find and add any "implicit" parameters that are thread unsafe
2614 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2615 if (implicitexternsyncparams is not None):
2616 for elem in implicitexternsyncparams:
2617 paramdecl += ' // '
2618 paramdecl += elem.text
2619 paramdecl += ' must be externally synchronized between host accesses\n'
2620
2621 if (paramdecl == ''):
2622 return None
2623 else:
2624 return paramdecl
2625 def beginFile(self, genOpts):
2626 OutputGenerator.beginFile(self, genOpts)
2627 # C-specific
2628 #
2629 # Multiple inclusion protection & C++ wrappers.
2630 if (genOpts.protectFile and self.genOpts.filename):
2631 headerSym = '__' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename))
2632 write('#ifndef', headerSym, file=self.outFile)
2633 write('#define', headerSym, '1', file=self.outFile)
2634 self.newline()
2635 write('#ifdef __cplusplus', file=self.outFile)
2636 write('extern "C" {', file=self.outFile)
2637 write('#endif', file=self.outFile)
2638 self.newline()
2639 #
2640 # User-supplied prefix text, if any (list of strings)
2641 if (genOpts.prefixText):
2642 for s in genOpts.prefixText:
2643 write(s, file=self.outFile)
2644 def endFile(self):
2645 # C-specific
2646 # Finish C++ wrapper and multiple inclusion protection
2647 self.newline()
2648 # record intercepted procedures
2649 write('// intercepts', file=self.outFile)
2650 write('struct { const char* name; PFN_vkVoidFunction pFunc;} procmap[] = {', file=self.outFile)
2651 write('\n'.join(self.intercepts), file=self.outFile)
2652 write('};\n', file=self.outFile)
2653 self.newline()
2654 write('#ifdef __cplusplus', file=self.outFile)
2655 write('}', file=self.outFile)
2656 write('#endif', file=self.outFile)
2657 if (self.genOpts.protectFile and self.genOpts.filename):
2658 self.newline()
2659 write('#endif', file=self.outFile)
2660 # Finish processing in superclass
2661 OutputGenerator.endFile(self)
2662 def beginFeature(self, interface, emit):
2663 #write('// starting beginFeature', file=self.outFile)
2664 # Start processing in superclass
2665 OutputGenerator.beginFeature(self, interface, emit)
2666 # C-specific
2667 # Accumulate includes, defines, types, enums, function pointer typedefs,
2668 # end function prototypes separately for this feature. They're only
2669 # printed in endFeature().
2670 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2671 #write('// ending beginFeature', file=self.outFile)
2672 def endFeature(self):
2673 # C-specific
2674 # Actually write the interface to the output file.
2675 #write('// starting endFeature', file=self.outFile)
2676 if (self.emit):
2677 self.newline()
2678 if (self.genOpts.protectFeature):
2679 write('#ifndef', self.featureName, file=self.outFile)
2680 # If type declarations are needed by other features based on
2681 # this one, it may be necessary to suppress the ExtraProtect,
2682 # or move it below the 'for section...' loop.
2683 #write('// endFeature looking at self.featureExtraProtect', file=self.outFile)
2684 if (self.featureExtraProtect != None):
2685 write('#ifdef', self.featureExtraProtect, file=self.outFile)
2686 #write('#define', self.featureName, '1', file=self.outFile)
2687 for section in self.TYPE_SECTIONS:
2688 #write('// endFeature writing section'+section, file=self.outFile)
2689 contents = self.sections[section]
2690 if contents:
2691 write('\n'.join(contents), file=self.outFile)
2692 self.newline()
2693 #write('// endFeature looking at self.sections[command]', file=self.outFile)
2694 if (self.sections['command']):
2695 write('\n'.join(self.sections['command']), end='', file=self.outFile)
2696 self.newline()
2697 if (self.featureExtraProtect != None):
2698 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
2699 if (self.genOpts.protectFeature):
2700 write('#endif /*', self.featureName, '*/', file=self.outFile)
2701 # Finish processing in superclass
2702 OutputGenerator.endFeature(self)
2703 #write('// ending endFeature', file=self.outFile)
2704 #
2705 # Append a definition to the specified section
2706 def appendSection(self, section, text):
2707 # self.sections[section].append('SECTION: ' + section + '\n')
2708 self.sections[section].append(text)
2709 #
2710 # Type generation
2711 def genType(self, typeinfo, name):
2712 pass
2713 #
2714 # Struct (e.g. C "struct" type) generation.
2715 # This is a special case of the <type> tag where the contents are
2716 # interpreted as a set of <member> tags instead of freeform C
2717 # C type declarations. The <member> tags are just like <param>
2718 # tags - they are a declaration of a struct or union member.
2719 # Only simple member declarations are supported (no nested
2720 # structs etc.)
2721 def genStruct(self, typeinfo, typeName):
2722 OutputGenerator.genStruct(self, typeinfo, typeName)
2723 body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
2724 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
2725 for member in typeinfo.elem.findall('.//member'):
2726 body += self.makeCParamDecl(member, self.genOpts.alignFuncParam)
2727 body += ';\n'
2728 body += '} ' + typeName + ';\n'
2729 self.appendSection('struct', body)
2730 #
2731 # Group (e.g. C "enum" type) generation.
2732 # These are concatenated together with other types.
2733 def genGroup(self, groupinfo, groupName):
2734 pass
2735 # Enumerant generation
2736 # <enum> tags may specify their values in several ways, but are usually
2737 # just integers.
2738 def genEnum(self, enuminfo, name):
2739 pass
2740 #
2741 # Command generation
2742 def genCmd(self, cmdinfo, name):
2743 special_functions = [
2744 'vkGetDeviceProcAddr',
2745 'vkGetInstanceProcAddr',
2746 'vkCreateDevice',
2747 'vkDestroyDevice',
2748 'vkCreateInstance',
2749 'vkDestroyInstance',
2750 'vkEnumerateInstanceLayerProperties',
2751 'vkEnumerateInstanceExtensionProperties',
2752 'vkAllocateCommandBuffers',
2753 'vkFreeCommandBuffers',
2754 'vkCreateDebugReportCallbackEXT',
2755 'vkDestroyDebugReportCallbackEXT',
2756 ]
2757 if name in special_functions:
Michael Lentine584ccab2016-02-03 16:51:46 -06002758 self.intercepts += [ ' {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name) ]
Mike Stroyan8849f9a2015-11-02 15:30:20 -07002759 return
2760 if "KHR" in name:
2761 self.appendSection('command', '// TODO - not wrapping KHR function ' + name)
2762 return
2763 # Determine first if this function needs to be intercepted
2764 startthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'start')
2765 if startthreadsafety is None:
2766 return
2767 finishthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'finish')
2768 # record that the function will be intercepted
2769 if (self.featureExtraProtect != None):
2770 self.intercepts += [ '#ifdef %s' % self.featureExtraProtect ]
Michael Lentine584ccab2016-02-03 16:51:46 -06002771 self.intercepts += [ ' {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name) ]
Mike Stroyan8849f9a2015-11-02 15:30:20 -07002772 if (self.featureExtraProtect != None):
2773 self.intercepts += [ '#endif' ]
2774
2775 OutputGenerator.genCmd(self, cmdinfo, name)
2776 #
2777 decls = self.makeCDecls(cmdinfo.elem)
2778 self.appendSection('command', '')
2779 self.appendSection('command', decls[0][:-1])
2780 self.appendSection('command', '{')
2781 # setup common to call wrappers
2782 # first parameter is always dispatchable
2783 dispatchable_type = cmdinfo.elem.find('param/type').text
2784 dispatchable_name = cmdinfo.elem.find('param/name').text
2785 self.appendSection('command', ' dispatch_key key = get_dispatch_key('+dispatchable_name+');')
2786 self.appendSection('command', ' layer_data *my_data = get_my_data_ptr(key, layer_data_map);')
2787 if dispatchable_type in ["VkPhysicalDevice", "VkInstance"]:
2788 self.appendSection('command', ' VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table;')
2789 else:
2790 self.appendSection('command', ' VkLayerDispatchTable *pTable = my_data->device_dispatch_table;')
2791 # Declare result variable, if any.
2792 resulttype = cmdinfo.elem.find('proto/type')
2793 if (resulttype != None and resulttype.text == 'void'):
2794 resulttype = None
2795 if (resulttype != None):
2796 self.appendSection('command', ' ' + resulttype.text + ' result;')
2797 assignresult = 'result = '
2798 else:
2799 assignresult = ''
2800
2801 self.appendSection('command', str(startthreadsafety))
2802 params = cmdinfo.elem.findall('param/name')
2803 paramstext = ','.join([str(param.text) for param in params])
2804 API = cmdinfo.elem.attrib.get('name').replace('vk','pTable->',1)
2805 self.appendSection('command', ' ' + assignresult + API + '(' + paramstext + ');')
2806 self.appendSection('command', str(finishthreadsafety))
2807 # Return result variable, if any.
2808 if (resulttype != None):
2809 self.appendSection('command', ' return result;')
2810 self.appendSection('command', '}')
Dustin Gravesf69e3772016-02-11 10:10:14 -07002811
2812# ParamCheckerOutputGenerator - subclass of OutputGenerator.
2813# Generates param checker layer code.
2814#
2815# ---- methods ----
2816# ParamCheckerOutputGenerator(errFile, warnFile, diagFile) - args as for
2817# OutputGenerator. Defines additional internal state.
2818# ---- methods overriding base class ----
2819# beginFile(genOpts)
2820# endFile()
2821# beginFeature(interface, emit)
2822# endFeature()
2823# genType(typeinfo,name)
2824# genStruct(typeinfo,name)
2825# genGroup(groupinfo,name)
2826# genEnum(enuminfo, name)
2827# genCmd(cmdinfo)
2828class ParamCheckerOutputGenerator(OutputGenerator):
2829 """Generate ParamChecker code based on XML element attributes"""
2830 # This is an ordered list of sections in the header file.
2831 ALL_SECTIONS = ['command']
2832 def __init__(self,
2833 errFile = sys.stderr,
2834 warnFile = sys.stderr,
2835 diagFile = sys.stdout):
2836 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2837 self.INDENT_SPACES = 4
2838 # Commands to ignore
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002839 self.blacklist = [
Dustin Graves80c0dea2016-03-03 14:17:08 -07002840 'vkGetInstanceProcAddr',
2841 'vkGetDeviceProcAddr',
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002842 'vkEnumerateInstanceLayerProperties',
2843 'vkEnumerateInstanceExtensionsProperties',
2844 'vkEnumerateDeviceLayerProperties',
2845 'vkEnumerateDeviceExtensionsProperties',
2846 'vkCreateDebugReportCallbackEXT',
2847 'vkDebugReportMessageEXT']
Dustin Gravesd59852f2016-04-15 18:06:14 -06002848 # Validation conditions for some special case struct members that are conditionally validated
2849 self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
Dustin Gravesf69e3772016-02-11 10:10:14 -07002850 # Internal state - accumulators for different inner block text
2851 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002852 self.structNames = [] # List of Vulkan struct typenames
2853 self.stypes = [] # Values from the VkStructureType enumeration
2854 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
2855 self.commands = [] # List of CommandData records for all Vulkan commands
2856 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
Dustin Graves7633d692016-04-14 15:03:31 -06002857 self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06002858 self.enumRanges = dict() # Map of enum name to BEGIN/END range values
Dustin Gravesf69e3772016-02-11 10:10:14 -07002859 # Named tuples to store struct and command data
2860 self.StructType = namedtuple('StructType', ['name', 'value'])
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06002861 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
Dustin Gravesd59852f2016-04-15 18:06:14 -06002862 'isconst', 'isoptional', 'iscount', 'len', 'extstructs', 'condition', 'cdecl'])
Dustin Gravesf69e3772016-02-11 10:10:14 -07002863 self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl'])
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002864 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
Dustin Gravesf69e3772016-02-11 10:10:14 -07002865 #
2866 def incIndent(self, indent):
2867 inc = ' ' * self.INDENT_SPACES
2868 if indent:
2869 return indent + inc
2870 return inc
Dustin Graves80c0dea2016-03-03 14:17:08 -07002871 #
Dustin Gravesf69e3772016-02-11 10:10:14 -07002872 def decIndent(self, indent):
2873 if indent and (len(indent) > self.INDENT_SPACES):
2874 return indent[:-self.INDENT_SPACES]
2875 return ''
2876 #
2877 def beginFile(self, genOpts):
2878 OutputGenerator.beginFile(self, genOpts)
2879 # C-specific
2880 #
2881 # User-supplied prefix text, if any (list of strings)
2882 if (genOpts.prefixText):
2883 for s in genOpts.prefixText:
2884 write(s, file=self.outFile)
2885 #
2886 # Multiple inclusion protection & C++ wrappers.
2887 if (genOpts.protectFile and self.genOpts.filename):
2888 headerSym = re.sub('\.h', '_H', os.path.basename(self.genOpts.filename)).upper()
2889 write('#ifndef', headerSym, file=self.outFile)
2890 write('#define', headerSym, '1', file=self.outFile)
2891 self.newline()
2892 #
2893 # Headers
Dustin Gravescd99e562016-03-31 09:50:42 -06002894 write('#include <string>', file=self.outFile)
2895 self.newline()
Dustin Gravesf69e3772016-02-11 10:10:14 -07002896 write('#include "vulkan/vulkan.h"', file=self.outFile)
Dustin Graves2949a5a2016-03-08 17:48:20 -07002897 write('#include "vk_layer_extension_utils.h"', file=self.outFile)
Mark Lobodzinski1c333572016-03-17 15:08:18 -06002898 write('#include "parameter_validation_utils.h"', file=self.outFile)
Dustin Gravesf69e3772016-02-11 10:10:14 -07002899 #
2900 # Macros
2901 self.newline()
2902 write('#ifndef UNUSED_PARAMETER', file=self.outFile)
2903 write('#define UNUSED_PARAMETER(x) (void)(x)', file=self.outFile)
2904 write('#endif // UNUSED_PARAMETER', file=self.outFile)
2905 def endFile(self):
2906 # C-specific
2907 # Finish C++ wrapper and multiple inclusion protection
2908 self.newline()
2909 if (self.genOpts.protectFile and self.genOpts.filename):
2910 self.newline()
2911 write('#endif', file=self.outFile)
2912 # Finish processing in superclass
2913 OutputGenerator.endFile(self)
2914 def beginFeature(self, interface, emit):
2915 # Start processing in superclass
2916 OutputGenerator.beginFeature(self, interface, emit)
2917 # C-specific
2918 # Accumulate includes, defines, types, enums, function pointer typedefs,
2919 # end function prototypes separately for this feature. They're only
2920 # printed in endFeature().
2921 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002922 self.structNames = []
Dustin Gravesf69e3772016-02-11 10:10:14 -07002923 self.stypes = []
2924 self.structTypes = dict()
2925 self.commands = []
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002926 self.structMembers = []
Dustin Graves7633d692016-04-14 15:03:31 -06002927 self.validatedStructs = dict()
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06002928 self.enumRanges = dict()
Dustin Gravesf69e3772016-02-11 10:10:14 -07002929 def endFeature(self):
2930 # C-specific
2931 # Actually write the interface to the output file.
2932 if (self.emit):
2933 self.newline()
2934 # If type declarations are needed by other features based on
2935 # this one, it may be necessary to suppress the ExtraProtect,
2936 # or move it below the 'for section...' loop.
2937 if (self.featureExtraProtect != None):
2938 write('#ifdef', self.featureExtraProtect, file=self.outFile)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002939 # Generate the struct member checking code from the captured data
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002940 self.processStructMemberData()
2941 # Generate the command parameter checking code from the captured data
Dustin Gravesf69e3772016-02-11 10:10:14 -07002942 self.processCmdData()
2943 if (self.sections['command']):
2944 if (self.genOpts.protectProto):
2945 write(self.genOpts.protectProto,
2946 self.genOpts.protectProtoStr, file=self.outFile)
2947 write('\n'.join(self.sections['command']), end='', file=self.outFile)
2948 if (self.featureExtraProtect != None):
2949 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
2950 else:
2951 self.newline()
2952 # Finish processing in superclass
2953 OutputGenerator.endFeature(self)
2954 #
2955 # Append a definition to the specified section
2956 def appendSection(self, section, text):
2957 # self.sections[section].append('SECTION: ' + section + '\n')
2958 self.sections[section].append(text)
2959 #
2960 # Type generation
2961 def genType(self, typeinfo, name):
2962 OutputGenerator.genType(self, typeinfo, name)
2963 typeElem = typeinfo.elem
2964 # If the type is a struct type, traverse the imbedded <member> tags
2965 # generating a structure. Otherwise, emit the tag text.
2966 category = typeElem.get('category')
2967 if (category == 'struct' or category == 'union'):
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002968 self.structNames.append(name)
Dustin Gravesf69e3772016-02-11 10:10:14 -07002969 self.genStruct(typeinfo, name)
2970 #
2971 # Struct parameter check generation.
2972 # This is a special case of the <type> tag where the contents are
2973 # interpreted as a set of <member> tags instead of freeform C
2974 # C type declarations. The <member> tags are just like <param>
2975 # tags - they are a declaration of a struct or union member.
2976 # Only simple member declarations are supported (no nested
2977 # structs etc.)
2978 def genStruct(self, typeinfo, typeName):
2979 OutputGenerator.genStruct(self, typeinfo, typeName)
Dustin Gravesd59852f2016-04-15 18:06:14 -06002980 conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002981 members = typeinfo.elem.findall('.//member')
2982 #
2983 # Iterate over members once to get length parameters for arrays
2984 lens = set()
2985 for member in members:
2986 len = self.getLen(member)
2987 if len:
2988 lens.add(len)
2989 #
2990 # Generate member info
2991 membersInfo = []
2992 for member in members:
Dustin Gravesf69e3772016-02-11 10:10:14 -07002993 # Get the member's type and name
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002994 info = self.getTypeNameTuple(member)
2995 type = info[0]
2996 name = info[1]
2997 stypeValue = ''
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06002998 cdecl = self.makeCParamDecl(member, 0)
Dustin Gravesf69e3772016-02-11 10:10:14 -07002999 # Process VkStructureType
3000 if type == 'VkStructureType':
3001 # Extract the required struct type value from the comments
3002 # embedded in the original text defining the 'typeinfo' element
3003 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003004 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
Dustin Gravesf69e3772016-02-11 10:10:14 -07003005 if result:
3006 value = result.group(0)
3007 # Make sure value is valid
3008 #if value not in self.stypes:
3009 # print('WARNING: {} is not part of the VkStructureType enumeration [{}]'.format(value, typeName))
3010 else:
Mike Stroyan0cf28a22016-04-05 16:40:30 -06003011 value = typeName
3012 # Remove EXT
3013 value = re.sub('EXT', '', value)
3014 # Add underscore between lowercase then uppercase
3015 value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', value)
3016 # Change to uppercase
3017 value = value.upper()
3018 # Add STRUCTURE_TYPE_
3019 value = re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003020 # Store the required type value
Dustin Gravesf69e3772016-02-11 10:10:14 -07003021 self.structTypes[typeName] = self.StructType(name=name, value=value)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003022 #
3023 # Store pointer/array/string info
3024 # Check for parameter name in lens set
3025 iscount = False
3026 if name in lens:
3027 iscount = True
3028 # The pNext members are not tagged as optional, but are treated as
3029 # optional for parameter NULL checks. Static array members
3030 # are also treated as optional to skip NULL pointer validation, as
3031 # they won't be NULL.
3032 isstaticarray = self.paramIsStaticArray(member)
3033 isoptional = False
3034 if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
3035 isoptional = True
3036 membersInfo.append(self.CommandParam(type=type, name=name,
3037 ispointer=self.paramIsPointer(member),
3038 isstaticarray=isstaticarray,
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003039 isbool=True if type == 'VkBool32' else False,
3040 israngedenum=True if type in self.enumRanges else False,
3041 isconst=True if 'const' in cdecl else False,
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003042 isoptional=isoptional,
3043 iscount=iscount,
3044 len=self.getLen(member),
Dustin Graves2949a5a2016-03-08 17:48:20 -07003045 extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None,
Dustin Gravesd59852f2016-04-15 18:06:14 -06003046 condition=conditions[name] if conditions and name in conditions else None,
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003047 cdecl=cdecl))
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003048 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
Dustin Gravesf69e3772016-02-11 10:10:14 -07003049 #
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003050 # Capture group (e.g. C "enum" type) info to be used for
3051 # param check code generation.
Dustin Gravesf69e3772016-02-11 10:10:14 -07003052 # These are concatenated together with other types.
3053 def genGroup(self, groupinfo, groupName):
3054 OutputGenerator.genGroup(self, groupinfo, groupName)
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003055 groupElem = groupinfo.elem
3056 #
3057 # Store the sType values
Dustin Gravesf69e3772016-02-11 10:10:14 -07003058 if groupName == 'VkStructureType':
Dustin Gravesf69e3772016-02-11 10:10:14 -07003059 for elem in groupElem.findall('enum'):
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003060 self.stypes.append(elem.get('name'))
3061 else:
3062 # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
3063 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
3064 expandPrefix = expandName
3065 expandSuffix = ''
3066 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
3067 if expandSuffixMatch:
3068 expandSuffix = '_' + expandSuffixMatch.group()
3069 # Strip off the suffix from the prefix
3070 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
3071 isEnum = ('FLAG_BITS' not in expandPrefix)
3072 if isEnum:
3073 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
Dustin Gravesf69e3772016-02-11 10:10:14 -07003074 #
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003075 # Capture command parameter info to be used for param
3076 # check code generation.
Dustin Gravesf69e3772016-02-11 10:10:14 -07003077 def genCmd(self, cmdinfo, name):
3078 OutputGenerator.genCmd(self, cmdinfo, name)
3079 if name not in self.blacklist:
Dustin Gravesf69e3772016-02-11 10:10:14 -07003080 params = cmdinfo.elem.findall('param')
Dustin Gravesf69e3772016-02-11 10:10:14 -07003081 # Get list of array lengths
3082 lens = set()
3083 for param in params:
3084 len = self.getLen(param)
3085 if len:
3086 lens.add(len)
3087 # Get param info
3088 paramsInfo = []
3089 for param in params:
3090 paramInfo = self.getTypeNameTuple(param)
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003091 cdecl = self.makeCParamDecl(param, 0)
Dustin Gravesf69e3772016-02-11 10:10:14 -07003092 # Check for parameter name in lens set
3093 iscount = False
3094 if paramInfo[1] in lens:
3095 iscount = True
3096 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
3097 ispointer=self.paramIsPointer(param),
3098 isstaticarray=self.paramIsStaticArray(param),
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003099 isbool=True if paramInfo[0] == 'VkBool32' else False,
3100 israngedenum=True if paramInfo[0] in self.enumRanges else False,
3101 isconst=True if 'const' in cdecl else False,
Dustin Gravesf69e3772016-02-11 10:10:14 -07003102 isoptional=self.paramIsOptional(param),
3103 iscount=iscount,
3104 len=self.getLen(param),
Dustin Graves2949a5a2016-03-08 17:48:20 -07003105 extstructs=None,
Dustin Gravesd59852f2016-04-15 18:06:14 -06003106 condition=None,
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003107 cdecl=cdecl))
Dustin Gravesf69e3772016-02-11 10:10:14 -07003108 self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0]))
3109 #
3110 # Check if the parameter passed in is a pointer
3111 def paramIsPointer(self, param):
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003112 ispointer = 0
Dustin Gravesf69e3772016-02-11 10:10:14 -07003113 paramtype = param.find('type')
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003114 if (paramtype.tail is not None) and ('*' in paramtype.tail):
3115 ispointer = paramtype.tail.count('*')
Dustin Graves3e646e32016-03-07 17:52:14 -07003116 elif paramtype.text[:4] == 'PFN_':
3117 # Treat function pointer typedefs as a pointer to a single value
3118 ispointer = 1
Dustin Gravesf69e3772016-02-11 10:10:14 -07003119 return ispointer
3120 #
3121 # Check if the parameter passed in is a static array
3122 def paramIsStaticArray(self, param):
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003123 isstaticarray = 0
3124 paramname = param.find('name')
3125 if (paramname.tail is not None) and ('[' in paramname.tail):
3126 isstaticarray = paramname.tail.count('[')
Dustin Gravesf69e3772016-02-11 10:10:14 -07003127 return isstaticarray
3128 #
3129 # Check if the parameter passed in is optional
3130 # Returns a list of Boolean values for comma separated len attributes (len='false,true')
3131 def paramIsOptional(self, param):
3132 # See if the handle is optional
3133 isoptional = False
3134 # Simple, if it's optional, return true
3135 optString = param.attrib.get('optional')
3136 if optString:
3137 if optString == 'true':
3138 isoptional = True
3139 elif ',' in optString:
3140 opts = []
3141 for opt in optString.split(','):
3142 val = opt.strip()
3143 if val == 'true':
3144 opts.append(True)
3145 elif val == 'false':
3146 opts.append(False)
3147 else:
3148 print('Unrecognized len attribute value',val)
3149 isoptional = opts
3150 return isoptional
3151 #
3152 # Retrieve the value of the len tag
3153 def getLen(self, param):
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003154 result = None
Dustin Gravesf69e3772016-02-11 10:10:14 -07003155 len = param.attrib.get('len')
3156 if len and len != 'null-terminated':
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003157 # For string arrays, 'len' can look like 'count,null-terminated',
3158 # indicating that we have a null terminated array of strings. We
3159 # strip the null-terminated from the 'len' field and only return
3160 # the parameter specifying the string count
3161 if 'null-terminated' in len:
3162 result = len.split(',')[0]
3163 else:
3164 result = len
3165 return result
Dustin Gravesf69e3772016-02-11 10:10:14 -07003166 #
3167 # Retrieve the type and name for a parameter
3168 def getTypeNameTuple(self, param):
3169 type = ''
3170 name = ''
3171 for elem in param:
3172 if elem.tag == 'type':
3173 type = noneStr(elem.text)
3174 elif elem.tag == 'name':
3175 name = noneStr(elem.text)
3176 return (type, name)
3177 #
3178 # Find a named parameter in a parameter list
3179 def getParamByName(self, params, name):
3180 for param in params:
3181 if param.name == name:
3182 return param
3183 return None
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003184 #
Dustin Gravesb8458d12016-03-28 16:17:38 -06003185 # Extract length values from latexmath. Currently an inflexible solution that looks for specific
3186 # patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
3187 def parseLateXMath(self, source):
3188 name = 'ERROR'
3189 decoratedName = 'ERROR'
3190 if 'mathit' in source:
3191 # Matches expressions similar to 'latexmath:[$\lceil{\mathit{rasterizationSamples} \over 32}\rceil$]'
3192 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)
3193 if not match or match.group(1) != match.group(4):
3194 raise 'Unrecognized latexmath expression'
3195 name = match.group(2)
3196 decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
3197 else:
3198 # Matches expressions similar to 'latexmath : [$dataSize \over 4$]'
3199 match = re.match(r'latexmath\s*\:\s*\[\s*\$\s*(\w+)\s*\\over\s*(\d+)\s*\$\s*\]', source)
3200 name = match.group(1)
3201 decoratedName = '{}/{}'.format(*match.group(1, 2))
3202 return name, decoratedName
3203 #
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003204 # Get the length paramater record for the specified parameter name
3205 def getLenParam(self, params, name):
3206 lenParam = None
3207 if name:
3208 if '->' in name:
3209 # The count is obtained by dereferencing a member of a struct parameter
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003210 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
Dustin Gravesd59852f2016-04-15 18:06:14 -06003211 isstaticarray=None, isoptional=False, type=None, len=None, extstructs=None, condition=None, cdecl=None)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003212 elif 'latexmath' in name:
Dustin Gravesb8458d12016-03-28 16:17:38 -06003213 lenName, decoratedName = self.parseLateXMath(name)
3214 lenParam = self.getParamByName(params, lenName)
3215 # TODO: Zero-check the result produced by the equation?
3216 # Copy the stored len parameter entry and overwrite the name with the processed latexmath equation
3217 #param = self.getParamByName(params, lenName)
3218 #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer,
3219 # isoptional=param.isoptional, type=param.type, len=param.len,
Dustin Gravesd59852f2016-04-15 18:06:14 -06003220 # isstaticarray=param.isstaticarray, extstructs=param.extstructs,
3221 # condition=None, cdecl=param.cdecl)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003222 else:
3223 lenParam = self.getParamByName(params, name)
3224 return lenParam
3225 #
Mark Lobodzinski1c333572016-03-17 15:08:18 -06003226 # Convert a vulkan.h command declaration into a parameter_validation.h definition
Dustin Gravesf69e3772016-02-11 10:10:14 -07003227 def getCmdDef(self, cmd):
Dustin Gravesf69e3772016-02-11 10:10:14 -07003228 #
3229 # Strip the trailing ';' and split into individual lines
3230 lines = cmd.cdecl[:-1].split('\n')
3231 # Replace Vulkan prototype
Dustin Gravesbb849942016-04-05 13:48:15 -06003232 lines[0] = 'static bool parameter_validation_' + cmd.name + '('
Dustin Graves80c0dea2016-03-03 14:17:08 -07003233 # Replace the first argument with debug_report_data, when the first
3234 # argument is a handle (not vkCreateInstance)
3235 reportData = ' debug_report_data*'.ljust(self.genOpts.alignFuncParam) + 'report_data,'
3236 if cmd.name != 'vkCreateInstance':
3237 lines[1] = reportData
3238 else:
3239 lines.insert(1, reportData)
Dustin Gravesf69e3772016-02-11 10:10:14 -07003240 return '\n'.join(lines)
3241 #
Dustin Gravesaf1f1b82016-02-29 13:35:07 -07003242 # Generate the code to check for a NULL dereference before calling the
3243 # validation function
Dustin Graves62448bf2016-04-11 16:06:25 -06003244 def genCheckedLengthCall(self, name, exprs):
Dustin Gravesaf1f1b82016-02-29 13:35:07 -07003245 count = name.count('->')
3246 if count:
Dustin Graves62448bf2016-04-11 16:06:25 -06003247 checkedExpr = []
3248 localIndent = ''
Dustin Gravesaf1f1b82016-02-29 13:35:07 -07003249 elements = name.split('->')
3250 # Open the if expression blocks
3251 for i in range(0, count):
Dustin Graves62448bf2016-04-11 16:06:25 -06003252 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
Dustin Gravesaf1f1b82016-02-29 13:35:07 -07003253 localIndent = self.incIndent(localIndent)
3254 # Add the validation expression
Dustin Graves62448bf2016-04-11 16:06:25 -06003255 for expr in exprs:
3256 checkedExpr.append(localIndent + expr)
Dustin Gravesaf1f1b82016-02-29 13:35:07 -07003257 # Close the if blocks
3258 for i in range(0, count):
3259 localIndent = self.decIndent(localIndent)
Dustin Graves62448bf2016-04-11 16:06:25 -06003260 checkedExpr.append(localIndent + '}\n')
3261 return [checkedExpr]
Dustin Gravesaf1f1b82016-02-29 13:35:07 -07003262 # No if statements were required
Dustin Graves62448bf2016-04-11 16:06:25 -06003263 return exprs
3264 #
Dustin Gravesd59852f2016-04-15 18:06:14 -06003265 # Generate code to check for a specific condition before executing validation code
3266 def genConditionalCall(self, prefix, condition, exprs):
3267 checkedExpr = []
3268 localIndent = ''
3269 formattedCondition = condition.format(prefix)
3270 checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
3271 checkedExpr.append(localIndent + '{\n')
3272 localIndent = self.incIndent(localIndent)
3273 for expr in exprs:
3274 checkedExpr.append(localIndent + expr)
3275 localIndent = self.decIndent(localIndent)
3276 checkedExpr.append(localIndent + '}\n')
3277 return [checkedExpr]
3278 #
Dustin Graves62448bf2016-04-11 16:06:25 -06003279 # Generate the sType check string
3280 def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName):
3281 checkExpr = []
3282 stype = self.structTypes[value.type]
3283 if lenValue:
3284 # This is an array with a pointer to a count value
3285 if lenValue.ispointer:
3286 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Dustin Graves7633d692016-04-14 15:03:31 -06003287 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", "{ldn}", "{dn}", "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format(
Dustin Graves62448bf2016-04-11 16:06:25 -06003288 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix))
3289 # This is an array with an integer count value
3290 else:
Dustin Graves7633d692016-04-14 15:03:31 -06003291 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", "{ldn}", "{dn}", "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {});\n'.format(
Dustin Graves62448bf2016-04-11 16:06:25 -06003292 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix))
3293 # This is an individual struct
3294 else:
Dustin Graves7633d692016-04-14 15:03:31 -06003295 checkExpr.append('skipCall |= validate_struct_type(report_data, "{}", "{}", "{sv}", {}{vn}, {sv}, {});\n'.format(
Dustin Graves62448bf2016-04-11 16:06:25 -06003296 funcPrintName, valuePrintName, prefix, valueRequired, vn=value.name, sv=stype.value))
3297 return checkExpr
3298 #
3299 # Generate pNext check string
3300 def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName):
3301 checkExpr = []
3302 # Generate an array of acceptable VkStructureType values for pNext
3303 extStructCount = 0
3304 extStructVar = 'NULL'
3305 extStructNames = 'NULL'
3306 if value.extstructs:
3307 structs = value.extstructs.split(',')
3308 checkExpr.append('const VkStructureType allowedStructs[] = {' + ', '.join([self.structTypes[s].value for s in structs]) + '};\n')
3309 extStructCount = 'ARRAY_SIZE(allowedStructs)'
3310 extStructVar = 'allowedStructs'
3311 extStructNames = '"' + ', '.join(structs) + '"'
Dustin Graves7633d692016-04-14 15:03:31 -06003312 checkExpr.append('skipCall |= validate_struct_pnext(report_data, "{}", "{}", {}, {}{}, {}, {});\n'.format(
Dustin Graves62448bf2016-04-11 16:06:25 -06003313 funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar))
3314 return checkExpr
3315 #
3316 # Generate the pointer check string
3317 def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName):
3318 checkExpr = []
3319 if lenValue:
3320 # This is an array with a pointer to a count value
3321 if lenValue.ispointer:
3322 # If count and array parameters are optional, there will be no validation
3323 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
3324 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Dustin Graves7633d692016-04-14 15:03:31 -06003325 checkExpr.append('skipCall |= validate_array(report_data, "{}", "{ldn}", "{dn}", {pf}{ln}, {pf}{vn}, {}, {}, {});\n'.format(
Dustin Graves62448bf2016-04-11 16:06:25 -06003326 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix))
3327 # This is an array with an integer count value
3328 else:
3329 # If count and array parameters are optional, there will be no validation
3330 if valueRequired == 'true' or lenValueRequired == 'true':
3331 # Arrays of strings receive special processing
Dustin Graves7633d692016-04-14 15:03:31 -06003332 validationFuncName = 'validate_array' if value.type != 'char' else 'validate_string_array'
3333 checkExpr.append('skipCall |= {}(report_data, "{}", "{ldn}", "{dn}", {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
3334 validationFuncName, funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix))
Dustin Graves62448bf2016-04-11 16:06:25 -06003335 if checkExpr:
3336 if lenValue and ('->' in lenValue.name):
3337 # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
3338 checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
3339 # This is an individual struct that is not allowed to be NULL
3340 elif not value.isoptional:
3341 # Function pointers need a reinterpret_cast to void*
3342 if value.type[:4] == 'PFN_':
Dustin Graves7633d692016-04-14 15:03:31 -06003343 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", "{}", reinterpret_cast<const void*>({}{}));\n'.format(funcPrintName, valuePrintName, prefix, value.name))
Dustin Graves62448bf2016-04-11 16:06:25 -06003344 else:
Dustin Graves7633d692016-04-14 15:03:31 -06003345 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", "{}", {}{});\n'.format(funcPrintName, valuePrintName, prefix, value.name))
Dustin Graves62448bf2016-04-11 16:06:25 -06003346 return checkExpr
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003347 #
Dustin Graves7633d692016-04-14 15:03:31 -06003348 # Process struct member validation code, performing name suibstitution if required
3349 def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix):
3350 if any(token in line for token in ['{funcName}', '{valuePrefix}', '{displayNamePrefix}']):
3351 return line.format(funcName=funcName, valuePrefix=memberNamePrefix, displayNamePrefix=memberDisplayNamePrefix)
3352 return line
3353 #
3354 # Process struct validation code for inclusion in function or parent struct validation code
3355 def expandStructCode(self, lines, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output):
3356 for line in lines:
3357 if output:
3358 output[-1] += '\n'
3359 if type(line) is list:
3360 for sub in line:
3361 output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix))
3362 else:
3363 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix))
3364 return output
3365 #
3366 # Process struct pointer/array validation code, perfoeming name substitution if required
3367 def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName):
3368 expr = []
3369 expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
3370 expr.append('{')
3371 indent = self.incIndent(None)
3372 if lenValue:
3373 # Need to process all elements in the array
3374 indexName = lenValue.name.replace('Count', 'Index')
3375 expr[-1] += '\n'
3376 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
3377 expr.append(indent + '{')
3378 indent = self.incIndent(indent)
3379 # Prefix for value name to display in error message
3380 memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
3381 memberDisplayNamePrefix = '{}[i].'.format(valueDisplayName)
3382 else:
3383 memberNamePrefix = '{}{}->'.format(prefix, value.name)
3384 memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
3385 #
3386 # Expand the struct validation lines
3387 expr = self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr)
3388 #
3389 if lenValue:
3390 # Close if and for scopes
3391 indent = self.decIndent(indent)
3392 expr.append(indent + '}\n')
3393 expr.append('}\n')
3394 return expr
3395 #
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003396 # Generate the parameter checking code
Dustin Graves62448bf2016-04-11 16:06:25 -06003397 def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
3398 lines = [] # Generated lines of code
3399 unused = [] # Unused variable names
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003400 for value in values:
Dustin Graves7633d692016-04-14 15:03:31 -06003401 usedLines = []
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003402 lenParam = None
3403 #
Dustin Graves62448bf2016-04-11 16:06:25 -06003404 # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
Dustin Graves7633d692016-04-14 15:03:31 -06003405 valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003406 #
3407 # Check for NULL pointers, ignore the inout count parameters that
3408 # will be validated with their associated array
3409 if (value.ispointer or value.isstaticarray) and not value.iscount:
3410 #
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003411 # Parameters for function argument generation
Dustin Graves62448bf2016-04-11 16:06:25 -06003412 req = 'true' # Paramerter cannot be NULL
3413 cpReq = 'true' # Count pointer cannot be NULL
3414 cvReq = 'true' # Count value cannot be 0
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003415 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003416 #
3417 # Generate required/optional parameter strings for the pointer and count values
3418 if value.isoptional:
Dustin Gravesbb849942016-04-05 13:48:15 -06003419 req = 'false'
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003420 if value.len:
3421 # The parameter is an array with an explicit count parameter
3422 lenParam = self.getLenParam(values, value.len)
Dustin Graves7633d692016-04-14 15:03:31 -06003423 lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003424 if lenParam.ispointer:
3425 # Count parameters that are pointers are inout
3426 if type(lenParam.isoptional) is list:
3427 if lenParam.isoptional[0]:
Dustin Gravesbb849942016-04-05 13:48:15 -06003428 cpReq = 'false'
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003429 if lenParam.isoptional[1]:
Dustin Gravesbb849942016-04-05 13:48:15 -06003430 cvReq = 'false'
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003431 else:
3432 if lenParam.isoptional:
Dustin Gravesbb849942016-04-05 13:48:15 -06003433 cpReq = 'false'
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003434 else:
3435 if lenParam.isoptional:
Dustin Gravesbb849942016-04-05 13:48:15 -06003436 cvReq = 'false'
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003437 #
3438 # If this is a pointer to a struct with an sType field, verify the type
3439 if value.type in self.structTypes:
Dustin Graves7633d692016-04-14 15:03:31 -06003440 usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName)
Dustin Graves2949a5a2016-03-08 17:48:20 -07003441 elif value.name == 'pNext':
3442 # We need to ignore VkDeviceCreateInfo and VkInstanceCreateInfo, as the loader manipulates them in a way that is not documented in vk.xml
Dustin Graves62448bf2016-04-11 16:06:25 -06003443 if not structTypeName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']:
Dustin Graves7633d692016-04-14 15:03:31 -06003444 usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003445 else:
Dustin Graves7633d692016-04-14 15:03:31 -06003446 usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003447 #
Dustin Gravescd99e562016-03-31 09:50:42 -06003448 # If this is a pointer to a struct (input), see if it contains members that need to be checked
3449 if value.type in self.validatedStructs and value.isconst:
Dustin Graves7633d692016-04-14 15:03:31 -06003450 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName))
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003451 elif value.isbool and value.isconst:
Dustin Graves7633d692016-04-14 15:03:31 -06003452 usedLines.append('skipCall |= validate_bool32_array(report_data, "{}", "{}", {pf}{}, {pf}{});\n'.format(funcName, valueDisplayName, lenParam.name, value.name, pf=valuePrefix))
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003453 elif value.israngedenum and value.isconst:
3454 enumRange = self.enumRanges[value.type]
Dustin Graves7633d692016-04-14 15:03:31 -06003455 usedLines.append('skipCall |= validate_ranged_enum_array(report_data, "{}", "{}", "{}", {}, {}, {pf}{}, {pf}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], lenParam.name, value.name, pf=valuePrefix))
3456 # Non-pointer types
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003457 elif value.type in self.validatedStructs:
Dustin Graves7633d692016-04-14 15:03:31 -06003458 memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
3459 memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
3460 usedLines.append(self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, '', []))
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003461 elif value.isbool:
Dustin Graves7633d692016-04-14 15:03:31 -06003462 usedLines.append('skipCall |= validate_bool32(report_data, "{}", "{}", {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name))
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003463 elif value.israngedenum:
3464 enumRange = self.enumRanges[value.type]
Dustin Graves7633d692016-04-14 15:03:31 -06003465 usedLines.append('skipCall |= validate_ranged_enum(report_data, "{}", "{}", "{}", {}, {}, {}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], valuePrefix, value.name))
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003466 #
3467 # Append the parameter check to the function body for the current command
Dustin Graves7633d692016-04-14 15:03:31 -06003468 if usedLines:
Dustin Gravesd59852f2016-04-15 18:06:14 -06003469 # Apply special conditional checks
3470 if value.condition:
3471 usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
Dustin Graves7633d692016-04-14 15:03:31 -06003472 lines += usedLines
Dustin Graves80c0dea2016-03-03 14:17:08 -07003473 elif not value.iscount:
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003474 # If no expression was generated for this value, it is unreferenced by the validation function, unless
3475 # it is an array count, which is indirectly referenced for array valiadation.
Dustin Graves80c0dea2016-03-03 14:17:08 -07003476 unused.append(value.name)
Dustin Graves62448bf2016-04-11 16:06:25 -06003477 return lines, unused
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003478 #
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003479 # Generate the struct member check code from the captured data
3480 def processStructMemberData(self):
3481 indent = self.incIndent(None)
3482 for struct in self.structMembers:
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003483 #
3484 # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
Dustin Graves7633d692016-04-14 15:03:31 -06003485 lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
Dustin Graves62448bf2016-04-11 16:06:25 -06003486 if lines:
Dustin Graves7633d692016-04-14 15:03:31 -06003487 self.validatedStructs[struct.name] = lines
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003488 #
3489 # Generate the command param check code from the captured data
3490 def processCmdData(self):
3491 indent = self.incIndent(None)
3492 for command in self.commands:
Dustin Graves7633d692016-04-14 15:03:31 -06003493 lines, unused = self.genFuncBody(command.name, command.params, '', '', None)
Dustin Graves62448bf2016-04-11 16:06:25 -06003494 if lines:
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003495 cmdDef = self.getCmdDef(command) + '\n'
3496 cmdDef += '{\n'
Dustin Graves62448bf2016-04-11 16:06:25 -06003497 # Process unused parameters, Ignoring the first dispatch handle parameter, which is not
3498 # processed by parameter_validation (except for vkCreateInstance, which does not have a
3499 # handle as its first parameter)
3500 if unused:
3501 startIndex = 0 if command.name == 'vkCreateInstance' else 1
3502 for name in unused[startIndex:]:
3503 cmdDef += indent + 'UNUSED_PARAMETER({});\n'.format(name)
3504 if len(unused) > startIndex:
3505 cmdDef += '\n'
Dustin Gravesbb849942016-04-05 13:48:15 -06003506 cmdDef += indent + 'bool skipCall = false;\n'
Dustin Graves62448bf2016-04-11 16:06:25 -06003507 for line in lines:
3508 cmdDef += '\n'
3509 if type(line) is list:
3510 for sub in line:
3511 cmdDef += indent + sub
3512 else:
3513 cmdDef += indent + line
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003514 cmdDef += '\n'
3515 cmdDef += indent + 'return skipCall;\n'
3516 cmdDef += '}\n'
3517 self.appendSection('command', cmdDef)