blob: 84d7edc1dac21368c45d9ce5a114a370fdecdd52 [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
26from lxml import 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.
35# Used in converting lxml Elements into text.
36# 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.
682 # lxml.etree has elem.text followed by (elem[i], elem[i].tail)
683 # for each child element and any following text
684 # Leading text
685 pdecl += noneStr(proto.text)
686 tdecl += noneStr(proto.text)
687 # For each child element, if it's a <name> wrap in appropriate
688 # declaration. Otherwise append its contents and tail contents.
689 for elem in proto:
690 text = noneStr(elem.text)
691 tail = noneStr(elem.tail)
692 if (elem.tag == 'name'):
693 pdecl += self.makeProtoName(text, tail)
694 tdecl += self.makeTypedefName(text, tail)
695 else:
696 pdecl += text + tail
697 tdecl += text + tail
698 # Now add the parameter declaration list, which is identical
699 # for prototypes and typedefs. Concatenate all the text from
700 # a <param> node without the tags. No tree walking required
701 # since all tags are ignored.
702 # Uses: self.indentFuncProto
703 # self.indentFuncPointer
704 # self.alignFuncParam
705 # Might be able to doubly-nest the joins, e.g.
706 # ','.join(('_'.join([l[i] for i in range(0,len(l))])
707 n = len(params)
708 # Indented parameters
709 if n > 0:
710 indentdecl = '(\n'
711 for i in range(0,n):
712 paramdecl = self.makeCParamDecl(params[i], self.genOpts.alignFuncParam)
713 if (i < n - 1):
714 paramdecl += ',\n'
715 else:
716 paramdecl += ');'
717 indentdecl += paramdecl
718 else:
719 indentdecl = '(void);'
720 # Non-indented parameters
721 paramdecl = '('
722 if n > 0:
723 for i in range(0,n):
724 paramdecl += ''.join([t for t in params[i].itertext()])
725 if (i < n - 1):
726 paramdecl += ', '
727 else:
728 paramdecl += 'void'
729 paramdecl += ");";
730 return [ pdecl + indentdecl, tdecl + paramdecl ]
731 #
732 def newline(self):
733 write('', file=self.outFile)
734
735 def setRegistry(self, registry):
736 self.registry = registry
737 #
738
739# COutputGenerator - subclass of OutputGenerator.
740# Generates C-language API interfaces.
741#
742# ---- methods ----
743# COutputGenerator(errFile, warnFile, diagFile) - args as for
744# OutputGenerator. Defines additional internal state.
745# ---- methods overriding base class ----
746# beginFile(genOpts)
747# endFile()
748# beginFeature(interface, emit)
749# endFeature()
750# genType(typeinfo,name)
751# genStruct(typeinfo,name)
752# genGroup(groupinfo,name)
753# genEnum(enuminfo, name)
754# genCmd(cmdinfo)
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
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06002838 # Struct member categories, to be used to avoid validating output values retrieved by queries such as vkGetPhysicalDeviceProperties
2839 # For example, VkPhysicalDeviceProperties will be ignored for vkGetPhysicalDeviceProperties where it is an ouput, but will be processed
2840 # for vkCreateDevice where is it a member of the VkDeviceCreateInfo input parameter.
2841 self.STRUCT_MEMBERS_INPUT_ONLY_NONE = 1 # The struct contains no 'input-only' members and will always be processed
2842 self.STRUCT_MEMBERS_INPUT_ONLY_MIXED = 2 # The struct contains some 'input-only' members; these members will only be processed when the struct is an input parameter
2843 self.STRUCT_MEMBERS_INPUT_ONLY_EXCLUSIVE = 3 # The struct contains only 'input-only' members; the entire struct will only be processed when it is an input parameter
Dustin Gravesf69e3772016-02-11 10:10:14 -07002844 # Commands to ignore
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002845 self.blacklist = [
Dustin Graves80c0dea2016-03-03 14:17:08 -07002846 'vkGetInstanceProcAddr',
2847 'vkGetDeviceProcAddr',
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002848 'vkEnumerateInstanceLayerProperties',
2849 'vkEnumerateInstanceExtensionsProperties',
2850 'vkEnumerateDeviceLayerProperties',
2851 'vkEnumerateDeviceExtensionsProperties',
2852 'vkCreateDebugReportCallbackEXT',
2853 'vkDebugReportMessageEXT']
Dustin Gravesf69e3772016-02-11 10:10:14 -07002854 # Internal state - accumulators for different inner block text
2855 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002856 self.structNames = [] # List of Vulkan struct typenames
2857 self.stypes = [] # Values from the VkStructureType enumeration
2858 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
2859 self.commands = [] # List of CommandData records for all Vulkan commands
2860 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06002861 self.validatedStructs = dict() # Map of structs containing members that require validation to a value indicating
2862 # that the struct contains members that are only validated when it is an input parameter
2863 self.enumRanges = dict() # Map of enum name to BEGIN/END range values
Dustin Gravesf69e3772016-02-11 10:10:14 -07002864 # Named tuples to store struct and command data
2865 self.StructType = namedtuple('StructType', ['name', 'value'])
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06002866 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
2867 'isconst', 'isoptional', 'iscount', 'len', 'extstructs', 'cdecl'])
Dustin Gravesf69e3772016-02-11 10:10:14 -07002868 self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl'])
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002869 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
Dustin Gravesf69e3772016-02-11 10:10:14 -07002870 #
2871 def incIndent(self, indent):
2872 inc = ' ' * self.INDENT_SPACES
2873 if indent:
2874 return indent + inc
2875 return inc
Dustin Graves80c0dea2016-03-03 14:17:08 -07002876 #
Dustin Gravesf69e3772016-02-11 10:10:14 -07002877 def decIndent(self, indent):
2878 if indent and (len(indent) > self.INDENT_SPACES):
2879 return indent[:-self.INDENT_SPACES]
2880 return ''
2881 #
2882 def beginFile(self, genOpts):
2883 OutputGenerator.beginFile(self, genOpts)
2884 # C-specific
2885 #
2886 # User-supplied prefix text, if any (list of strings)
2887 if (genOpts.prefixText):
2888 for s in genOpts.prefixText:
2889 write(s, file=self.outFile)
2890 #
2891 # Multiple inclusion protection & C++ wrappers.
2892 if (genOpts.protectFile and self.genOpts.filename):
2893 headerSym = re.sub('\.h', '_H', os.path.basename(self.genOpts.filename)).upper()
2894 write('#ifndef', headerSym, file=self.outFile)
2895 write('#define', headerSym, '1', file=self.outFile)
2896 self.newline()
2897 #
2898 # Headers
Dustin Gravescd99e562016-03-31 09:50:42 -06002899 write('#include <string>', file=self.outFile)
2900 self.newline()
Dustin Gravesf69e3772016-02-11 10:10:14 -07002901 write('#include "vulkan/vulkan.h"', file=self.outFile)
Dustin Graves2949a5a2016-03-08 17:48:20 -07002902 write('#include "vk_layer_extension_utils.h"', file=self.outFile)
Mark Lobodzinski1c333572016-03-17 15:08:18 -06002903 write('#include "parameter_validation_utils.h"', file=self.outFile)
Dustin Gravesf69e3772016-02-11 10:10:14 -07002904 #
2905 # Macros
2906 self.newline()
2907 write('#ifndef UNUSED_PARAMETER', file=self.outFile)
2908 write('#define UNUSED_PARAMETER(x) (void)(x)', file=self.outFile)
2909 write('#endif // UNUSED_PARAMETER', file=self.outFile)
2910 def endFile(self):
2911 # C-specific
2912 # Finish C++ wrapper and multiple inclusion protection
2913 self.newline()
2914 if (self.genOpts.protectFile and self.genOpts.filename):
2915 self.newline()
2916 write('#endif', file=self.outFile)
2917 # Finish processing in superclass
2918 OutputGenerator.endFile(self)
2919 def beginFeature(self, interface, emit):
2920 # Start processing in superclass
2921 OutputGenerator.beginFeature(self, interface, emit)
2922 # C-specific
2923 # Accumulate includes, defines, types, enums, function pointer typedefs,
2924 # end function prototypes separately for this feature. They're only
2925 # printed in endFeature().
2926 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002927 self.structNames = []
Dustin Gravesf69e3772016-02-11 10:10:14 -07002928 self.stypes = []
2929 self.structTypes = dict()
2930 self.commands = []
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002931 self.structMembers = []
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06002932 self.validatedStructs = dict()
2933 self.enumRanges = dict()
Dustin Gravesf69e3772016-02-11 10:10:14 -07002934 def endFeature(self):
2935 # C-specific
2936 # Actually write the interface to the output file.
2937 if (self.emit):
2938 self.newline()
2939 # If type declarations are needed by other features based on
2940 # this one, it may be necessary to suppress the ExtraProtect,
2941 # or move it below the 'for section...' loop.
2942 if (self.featureExtraProtect != None):
2943 write('#ifdef', self.featureExtraProtect, file=self.outFile)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002944 # Generate the struct member checking code from the captured data
2945 self.prepareStructMemberData()
2946 self.processStructMemberData()
2947 # Generate the command parameter checking code from the captured data
Dustin Gravesf69e3772016-02-11 10:10:14 -07002948 self.processCmdData()
2949 if (self.sections['command']):
2950 if (self.genOpts.protectProto):
2951 write(self.genOpts.protectProto,
2952 self.genOpts.protectProtoStr, file=self.outFile)
2953 write('\n'.join(self.sections['command']), end='', file=self.outFile)
2954 if (self.featureExtraProtect != None):
2955 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
2956 else:
2957 self.newline()
2958 # Finish processing in superclass
2959 OutputGenerator.endFeature(self)
2960 #
2961 # Append a definition to the specified section
2962 def appendSection(self, section, text):
2963 # self.sections[section].append('SECTION: ' + section + '\n')
2964 self.sections[section].append(text)
2965 #
2966 # Type generation
2967 def genType(self, typeinfo, name):
2968 OutputGenerator.genType(self, typeinfo, name)
2969 typeElem = typeinfo.elem
2970 # If the type is a struct type, traverse the imbedded <member> tags
2971 # generating a structure. Otherwise, emit the tag text.
2972 category = typeElem.get('category')
2973 if (category == 'struct' or category == 'union'):
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002974 self.structNames.append(name)
Dustin Gravesf69e3772016-02-11 10:10:14 -07002975 self.genStruct(typeinfo, name)
2976 #
2977 # Struct parameter check generation.
2978 # This is a special case of the <type> tag where the contents are
2979 # interpreted as a set of <member> tags instead of freeform C
2980 # C type declarations. The <member> tags are just like <param>
2981 # tags - they are a declaration of a struct or union member.
2982 # Only simple member declarations are supported (no nested
2983 # structs etc.)
2984 def genStruct(self, typeinfo, typeName):
2985 OutputGenerator.genStruct(self, typeinfo, typeName)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002986 members = typeinfo.elem.findall('.//member')
2987 #
2988 # Iterate over members once to get length parameters for arrays
2989 lens = set()
2990 for member in members:
2991 len = self.getLen(member)
2992 if len:
2993 lens.add(len)
2994 #
2995 # Generate member info
2996 membersInfo = []
2997 for member in members:
Dustin Gravesf69e3772016-02-11 10:10:14 -07002998 # Get the member's type and name
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07002999 info = self.getTypeNameTuple(member)
3000 type = info[0]
3001 name = info[1]
3002 stypeValue = ''
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003003 cdecl = self.makeCParamDecl(member, 0)
Dustin Gravesf69e3772016-02-11 10:10:14 -07003004 # Process VkStructureType
3005 if type == 'VkStructureType':
3006 # Extract the required struct type value from the comments
3007 # embedded in the original text defining the 'typeinfo' element
3008 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003009 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
Dustin Gravesf69e3772016-02-11 10:10:14 -07003010 if result:
3011 value = result.group(0)
3012 # Make sure value is valid
3013 #if value not in self.stypes:
3014 # print('WARNING: {} is not part of the VkStructureType enumeration [{}]'.format(value, typeName))
3015 else:
3016 value = '<ERROR>'
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003017 # Store the required type value
Dustin Gravesf69e3772016-02-11 10:10:14 -07003018 self.structTypes[typeName] = self.StructType(name=name, value=value)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003019 #
3020 # Store pointer/array/string info
3021 # Check for parameter name in lens set
3022 iscount = False
3023 if name in lens:
3024 iscount = True
3025 # The pNext members are not tagged as optional, but are treated as
3026 # optional for parameter NULL checks. Static array members
3027 # are also treated as optional to skip NULL pointer validation, as
3028 # they won't be NULL.
3029 isstaticarray = self.paramIsStaticArray(member)
3030 isoptional = False
3031 if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
3032 isoptional = True
3033 membersInfo.append(self.CommandParam(type=type, name=name,
3034 ispointer=self.paramIsPointer(member),
3035 isstaticarray=isstaticarray,
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003036 isbool=True if type == 'VkBool32' else False,
3037 israngedenum=True if type in self.enumRanges else False,
3038 isconst=True if 'const' in cdecl else False,
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003039 isoptional=isoptional,
3040 iscount=iscount,
3041 len=self.getLen(member),
Dustin Graves2949a5a2016-03-08 17:48:20 -07003042 extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None,
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003043 cdecl=cdecl))
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003044 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
Dustin Gravesf69e3772016-02-11 10:10:14 -07003045 #
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003046 # Capture group (e.g. C "enum" type) info to be used for
3047 # param check code generation.
Dustin Gravesf69e3772016-02-11 10:10:14 -07003048 # These are concatenated together with other types.
3049 def genGroup(self, groupinfo, groupName):
3050 OutputGenerator.genGroup(self, groupinfo, groupName)
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003051 groupElem = groupinfo.elem
3052 #
3053 # Store the sType values
Dustin Gravesf69e3772016-02-11 10:10:14 -07003054 if groupName == 'VkStructureType':
Dustin Gravesf69e3772016-02-11 10:10:14 -07003055 for elem in groupElem.findall('enum'):
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003056 self.stypes.append(elem.get('name'))
3057 else:
3058 # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
3059 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
3060 expandPrefix = expandName
3061 expandSuffix = ''
3062 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
3063 if expandSuffixMatch:
3064 expandSuffix = '_' + expandSuffixMatch.group()
3065 # Strip off the suffix from the prefix
3066 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
3067 isEnum = ('FLAG_BITS' not in expandPrefix)
3068 if isEnum:
3069 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
Dustin Gravesf69e3772016-02-11 10:10:14 -07003070 #
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003071 # Capture command parameter info to be used for param
3072 # check code generation.
Dustin Gravesf69e3772016-02-11 10:10:14 -07003073 def genCmd(self, cmdinfo, name):
3074 OutputGenerator.genCmd(self, cmdinfo, name)
3075 if name not in self.blacklist:
Dustin Gravesf69e3772016-02-11 10:10:14 -07003076 params = cmdinfo.elem.findall('param')
Dustin Gravesf69e3772016-02-11 10:10:14 -07003077 # Get list of array lengths
3078 lens = set()
3079 for param in params:
3080 len = self.getLen(param)
3081 if len:
3082 lens.add(len)
3083 # Get param info
3084 paramsInfo = []
3085 for param in params:
3086 paramInfo = self.getTypeNameTuple(param)
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003087 cdecl = self.makeCParamDecl(param, 0)
Dustin Gravesf69e3772016-02-11 10:10:14 -07003088 # Check for parameter name in lens set
3089 iscount = False
3090 if paramInfo[1] in lens:
3091 iscount = True
3092 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
3093 ispointer=self.paramIsPointer(param),
3094 isstaticarray=self.paramIsStaticArray(param),
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003095 isbool=True if paramInfo[0] == 'VkBool32' else False,
3096 israngedenum=True if paramInfo[0] in self.enumRanges else False,
3097 isconst=True if 'const' in cdecl else False,
Dustin Gravesf69e3772016-02-11 10:10:14 -07003098 isoptional=self.paramIsOptional(param),
3099 iscount=iscount,
3100 len=self.getLen(param),
Dustin Graves2949a5a2016-03-08 17:48:20 -07003101 extstructs=None,
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003102 cdecl=cdecl))
Dustin Gravesf69e3772016-02-11 10:10:14 -07003103 self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0]))
3104 #
3105 # Check if the parameter passed in is a pointer
3106 def paramIsPointer(self, param):
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003107 ispointer = 0
Dustin Gravesf69e3772016-02-11 10:10:14 -07003108 paramtype = param.find('type')
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003109 if (paramtype.tail is not None) and ('*' in paramtype.tail):
3110 ispointer = paramtype.tail.count('*')
Dustin Graves3e646e32016-03-07 17:52:14 -07003111 elif paramtype.text[:4] == 'PFN_':
3112 # Treat function pointer typedefs as a pointer to a single value
3113 ispointer = 1
Dustin Gravesf69e3772016-02-11 10:10:14 -07003114 return ispointer
3115 #
3116 # Check if the parameter passed in is a static array
3117 def paramIsStaticArray(self, param):
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003118 isstaticarray = 0
3119 paramname = param.find('name')
3120 if (paramname.tail is not None) and ('[' in paramname.tail):
3121 isstaticarray = paramname.tail.count('[')
Dustin Gravesf69e3772016-02-11 10:10:14 -07003122 return isstaticarray
3123 #
3124 # Check if the parameter passed in is optional
3125 # Returns a list of Boolean values for comma separated len attributes (len='false,true')
3126 def paramIsOptional(self, param):
3127 # See if the handle is optional
3128 isoptional = False
3129 # Simple, if it's optional, return true
3130 optString = param.attrib.get('optional')
3131 if optString:
3132 if optString == 'true':
3133 isoptional = True
3134 elif ',' in optString:
3135 opts = []
3136 for opt in optString.split(','):
3137 val = opt.strip()
3138 if val == 'true':
3139 opts.append(True)
3140 elif val == 'false':
3141 opts.append(False)
3142 else:
3143 print('Unrecognized len attribute value',val)
3144 isoptional = opts
3145 return isoptional
3146 #
3147 # Retrieve the value of the len tag
3148 def getLen(self, param):
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003149 result = None
Dustin Gravesf69e3772016-02-11 10:10:14 -07003150 len = param.attrib.get('len')
3151 if len and len != 'null-terminated':
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003152 # For string arrays, 'len' can look like 'count,null-terminated',
3153 # indicating that we have a null terminated array of strings. We
3154 # strip the null-terminated from the 'len' field and only return
3155 # the parameter specifying the string count
3156 if 'null-terminated' in len:
3157 result = len.split(',')[0]
3158 else:
3159 result = len
3160 return result
Dustin Gravesf69e3772016-02-11 10:10:14 -07003161 #
3162 # Retrieve the type and name for a parameter
3163 def getTypeNameTuple(self, param):
3164 type = ''
3165 name = ''
3166 for elem in param:
3167 if elem.tag == 'type':
3168 type = noneStr(elem.text)
3169 elif elem.tag == 'name':
3170 name = noneStr(elem.text)
3171 return (type, name)
3172 #
3173 # Find a named parameter in a parameter list
3174 def getParamByName(self, params, name):
3175 for param in params:
3176 if param.name == name:
3177 return param
3178 return None
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003179 #
Dustin Gravesb8458d12016-03-28 16:17:38 -06003180 # Extract length values from latexmath. Currently an inflexible solution that looks for specific
3181 # patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
3182 def parseLateXMath(self, source):
3183 name = 'ERROR'
3184 decoratedName = 'ERROR'
3185 if 'mathit' in source:
3186 # Matches expressions similar to 'latexmath:[$\lceil{\mathit{rasterizationSamples} \over 32}\rceil$]'
3187 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)
3188 if not match or match.group(1) != match.group(4):
3189 raise 'Unrecognized latexmath expression'
3190 name = match.group(2)
3191 decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
3192 else:
3193 # Matches expressions similar to 'latexmath : [$dataSize \over 4$]'
3194 match = re.match(r'latexmath\s*\:\s*\[\s*\$\s*(\w+)\s*\\over\s*(\d+)\s*\$\s*\]', source)
3195 name = match.group(1)
3196 decoratedName = '{}/{}'.format(*match.group(1, 2))
3197 return name, decoratedName
3198 #
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003199 # Get the length paramater record for the specified parameter name
3200 def getLenParam(self, params, name):
3201 lenParam = None
3202 if name:
3203 if '->' in name:
3204 # The count is obtained by dereferencing a member of a struct parameter
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003205 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
3206 isstaticarray=None, isoptional=False, type=None, len=None, extstructs=None, cdecl=None)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003207 elif 'latexmath' in name:
Dustin Gravesb8458d12016-03-28 16:17:38 -06003208 lenName, decoratedName = self.parseLateXMath(name)
3209 lenParam = self.getParamByName(params, lenName)
3210 # TODO: Zero-check the result produced by the equation?
3211 # Copy the stored len parameter entry and overwrite the name with the processed latexmath equation
3212 #param = self.getParamByName(params, lenName)
3213 #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer,
3214 # isoptional=param.isoptional, type=param.type, len=param.len,
3215 # isstaticarray=param.isstaticarray, extstructs=param.extstructs, cdecl=param.cdecl)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003216 else:
3217 lenParam = self.getParamByName(params, name)
3218 return lenParam
3219 #
Mark Lobodzinski1c333572016-03-17 15:08:18 -06003220 # Convert a vulkan.h command declaration into a parameter_validation.h definition
Dustin Gravesf69e3772016-02-11 10:10:14 -07003221 def getCmdDef(self, cmd):
Dustin Gravesf69e3772016-02-11 10:10:14 -07003222 #
3223 # Strip the trailing ';' and split into individual lines
3224 lines = cmd.cdecl[:-1].split('\n')
3225 # Replace Vulkan prototype
Mark Lobodzinski1c333572016-03-17 15:08:18 -06003226 lines[0] = 'static VkBool32 parameter_validation_' + cmd.name + '('
Dustin Graves80c0dea2016-03-03 14:17:08 -07003227 # Replace the first argument with debug_report_data, when the first
3228 # argument is a handle (not vkCreateInstance)
3229 reportData = ' debug_report_data*'.ljust(self.genOpts.alignFuncParam) + 'report_data,'
3230 if cmd.name != 'vkCreateInstance':
3231 lines[1] = reportData
3232 else:
3233 lines.insert(1, reportData)
Dustin Gravesf69e3772016-02-11 10:10:14 -07003234 return '\n'.join(lines)
3235 #
Dustin Gravesaf1f1b82016-02-29 13:35:07 -07003236 # Generate the code to check for a NULL dereference before calling the
3237 # validation function
3238 def genCheckedLengthCall(self, indent, name, expr):
3239 count = name.count('->')
3240 if count:
3241 checkedExpr = ''
3242 localIndent = indent
3243 elements = name.split('->')
3244 # Open the if expression blocks
3245 for i in range(0, count):
3246 checkedExpr += localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1]))
3247 localIndent = self.incIndent(localIndent)
3248 # Add the validation expression
3249 checkedExpr += localIndent + expr
3250 # Close the if blocks
3251 for i in range(0, count):
3252 localIndent = self.decIndent(localIndent)
3253 checkedExpr += localIndent + '}\n'
3254 return checkedExpr
3255 # No if statements were required
3256 return indent + expr
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003257 #
3258 # Generate the parameter checking code
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003259 def genFuncBody(self, indent, name, values, valuePrefix, variablePrefix, structName, needConditionCheck):
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003260 funcBody = ''
3261 unused = []
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003262 # Code to conditionally check parameters only when they are inputs. Primarily avoids
3263 # checking uninitialized members of output structs used to retrieve bools and enums.
3264 # Conditional checks are grouped together to be appended to funcBody within a single
3265 # if check for input parameter direction.
3266 conditionalExprs = []
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003267 for value in values:
3268 checkExpr = '' # Code to check the current parameter
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003269 lenParam = None
3270 #
3271 # Generate the full name of the value, which will be printed in
3272 # the error message, by adding the variable prefix to the
3273 # value name
3274 valueDisplayName = '(std::string({}) + std::string("{}")).c_str()'.format(variablePrefix, value.name) if variablePrefix else '"{}"'.format(value.name)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003275 #
3276 # Check for NULL pointers, ignore the inout count parameters that
3277 # will be validated with their associated array
3278 if (value.ispointer or value.isstaticarray) and not value.iscount:
3279 #
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003280 # Parameters for function argument generation
3281 req = 'VK_TRUE' # Paramerter can be NULL
3282 cpReq = 'VK_TRUE' # Count pointer can be NULL
3283 cvReq = 'VK_TRUE' # Count value can be 0
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003284 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003285 #
3286 # Generate required/optional parameter strings for the pointer and count values
3287 if value.isoptional:
3288 req = 'VK_FALSE'
3289 if value.len:
3290 # The parameter is an array with an explicit count parameter
3291 lenParam = self.getLenParam(values, value.len)
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003292 lenDisplayName = '(std::string({}) + std::string("{}")).c_str()'.format(variablePrefix, lenParam.name) if variablePrefix else '"{}"'.format(lenParam.name)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003293 if lenParam.ispointer:
3294 # Count parameters that are pointers are inout
3295 if type(lenParam.isoptional) is list:
3296 if lenParam.isoptional[0]:
3297 cpReq = 'VK_FALSE'
3298 if lenParam.isoptional[1]:
3299 cvReq = 'VK_FALSE'
3300 else:
3301 if lenParam.isoptional:
3302 cpReq = 'VK_FALSE'
3303 else:
3304 if lenParam.isoptional:
3305 cvReq = 'VK_FALSE'
3306 #
3307 # If this is a pointer to a struct with an sType field, verify the type
3308 if value.type in self.structTypes:
3309 stype = self.structTypes[value.type]
3310 if lenParam:
3311 # This is an array
3312 if lenParam.ispointer:
3313 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003314 checkExpr = 'skipCall |= validate_struct_type_array(report_data, {}, {ldn}, {dn}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format(name, cpReq, cvReq, req, ln=lenParam.name, ldn=lenDisplayName, dn=valueDisplayName, vn=value.name, sv=stype.value, pf=valuePrefix)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003315 else:
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003316 checkExpr = 'skipCall |= validate_struct_type_array(report_data, {}, {ldn}, {dn}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {});\n'.format(name, cvReq, req, ln=lenParam.name, ldn=lenDisplayName, dn=valueDisplayName, vn=value.name, sv=stype.value, pf=valuePrefix)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003317 else:
3318 checkExpr = 'skipCall |= validate_struct_type(report_data, {}, {}, "{sv}", {}{vn}, {sv}, {});\n'.format(name, valueDisplayName, valuePrefix, req, vn=value.name, sv=stype.value)
Dustin Graves2949a5a2016-03-08 17:48:20 -07003319 elif value.name == 'pNext':
3320 # We need to ignore VkDeviceCreateInfo and VkInstanceCreateInfo, as the loader manipulates them in a way that is not documented in vk.xml
3321 if not structName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']:
3322 # Generate an array of acceptable VkStructureType values for pNext
3323 extStructCount = 0
3324 extStructVar = 'NULL'
3325 extStructNames = 'NULL'
3326 if value.extstructs:
3327 structs = value.extstructs.split(',')
3328 checkExpr = 'const VkStructureType allowedStructs[] = {' + ', '.join([self.structTypes[s].value for s in structs]) + '};\n' + indent
3329 extStructCount = 'ARRAY_SIZE(allowedStructs)'
3330 extStructVar = 'allowedStructs'
3331 extStructNames = '"' + ', '.join(structs) + '"'
3332 checkExpr += 'skipCall |= validate_struct_pnext(report_data, {}, {}, {}, {}{vn}, {}, {});\n'.format(name, valueDisplayName, extStructNames, valuePrefix, extStructCount, extStructVar, vn=value.name)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003333 else:
3334 if lenParam:
3335 # This is an array
3336 if lenParam.ispointer:
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003337 # If count and array parameters are optional, there will be no validation
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003338 if req == 'VK_TRUE' or cpReq == 'VK_TRUE' or cvReq == 'VK_TRUE':
3339 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003340 checkExpr = 'skipCall |= validate_array(report_data, {}, {ldn}, {dn}, {pf}{ln}, {pf}{vn}, {}, {}, {});\n'.format(name, cpReq, cvReq, req, ln=lenParam.name, ldn=lenDisplayName, dn=valueDisplayName, vn=value.name, pf=valuePrefix)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003341 else:
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003342 # If count and array parameters are optional, there will be no validation
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003343 if req == 'VK_TRUE' or cvReq == 'VK_TRUE':
Dustin Gravesc3fbcf82016-03-08 14:42:59 -07003344 funcName = 'validate_array' if value.type != 'char' else 'validate_string_array'
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003345 checkExpr = 'skipCall |= {}(report_data, {}, {ldn}, {dn}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(funcName, name, cvReq, req, ln=lenParam.name, ldn=lenDisplayName, dn=valueDisplayName, vn=value.name, pf=valuePrefix)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003346 elif not value.isoptional:
Dustin Graves3e646e32016-03-07 17:52:14 -07003347 # Function pointers need a reinterpret_cast to void*
3348 if value.type[:4] == 'PFN_':
3349 checkExpr = 'skipCall |= validate_required_pointer(report_data, {}, {}, reinterpret_cast<const void*>({}{vn}));\n'.format(name, valueDisplayName, valuePrefix, vn=value.name)
3350 else:
3351 checkExpr = 'skipCall |= validate_required_pointer(report_data, {}, {}, {}{vn});\n'.format(name, valueDisplayName, valuePrefix, vn=value.name)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003352 #
Dustin Gravescd99e562016-03-31 09:50:42 -06003353 # If this is a pointer to a struct (input), see if it contains members that need to be checked
3354 if value.type in self.validatedStructs and value.isconst:
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003355 #
3356 # The name prefix used when reporting an error with a struct member (eg. the 'pCreateInfor->' in 'pCreateInfo->sType')
Dustin Gravescd99e562016-03-31 09:50:42 -06003357 if lenParam:
3358 prefix = '(std::string({}) + std::string("{}[i]->")).c_str()'.format(variablePrefix, value.name) if variablePrefix else '(std::string("{}[i]->")).c_str()'.format(value.name)
3359 else:
3360 prefix = '(std::string({}) + std::string("{}->")).c_str()'.format(variablePrefix, value.name) if variablePrefix else '"{}->"'.format(value.name)
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003361 #
3362 membersInputOnly = self.validatedStructs[value.type]
3363 #
3364 # If the current struct has mixed 'input-only' and 'non-input-only' members, it needs an isInput flag
3365 if membersInputOnly == self.STRUCT_MEMBERS_INPUT_ONLY_MIXED:
3366 # If this function is called from another struct validation function (valuePrefix is not empty), then we forward the 'isInput' prameter
3367 isInput = 'isInput'
3368 if not valuePrefix:
3369 # We are validating function parameters and need to determine if the current value is an input parameter
3370 isInput = 'true' if value.isconst else 'false'
3371 if checkExpr:
3372 checkExpr += '\n' + indent
Dustin Gravescd99e562016-03-31 09:50:42 -06003373 if lenParam:
3374 # Need to process all elements in the array
3375 checkExpr += 'for (uint32_t i = 0; i < {}{}; ++i) {{\n'.format(valuePrefix, lenParam.name)
3376 indent = self.incIndent(indent)
3377 checkExpr += indent + 'skipCall |= parameter_validation_{}(report_data, {}, {}, {}, &({}{}[i]));\n'.format(value.type, name, prefix, isInput, valuePrefix, value.name)
3378 indent = self.decIndent(indent)
3379 checkExpr += indent + '}\n'
3380 else:
3381 checkExpr += 'skipCall |= parameter_validation_{}(report_data, {}, {}, {}, {}{});\n'.format(value.type, name, prefix, isInput, valuePrefix, value.name)
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003382 else:
3383 # Validation function does not have an isInput field
Dustin Gravescd99e562016-03-31 09:50:42 -06003384 if lenParam:
3385 # Need to process all elements in the array
3386 expr = 'for (uint32_t i = 0; i < {}{}; ++i) {{\n'.format(valuePrefix, lenParam.name)
3387 indent = self.incIndent(indent)
3388 expr += indent + 'skipCall |= parameter_validation_{}(report_data, {}, {}, &({}{}[i]));\n'.format(value.type, name, prefix, valuePrefix, value.name)
3389 indent = self.decIndent(indent)
3390 expr += indent + '}\n'
3391 else:
3392 expr = 'skipCall |= parameter_validation_{}(report_data, {}, {}, {}{});\n'.format(value.type, name, prefix, valuePrefix, value.name)
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003393 #
3394 # If the struct only has input-only members and is a member of another struct, it is conditionally processed based on 'isInput'
3395 if valuePrefix and membersInputOnly == self.STRUCT_MEMBERS_INPUT_ONLY_EXCLUSIVE:
3396 if needConditionCheck:
Dustin Gravescd99e562016-03-31 09:50:42 -06003397 if expr.count('\n') > 1:
3398 # TODO: Proper fix for this formatting workaround
3399 conditionalExprs.append(expr.replace(' ' * 8, ' ' * 12))
3400 else:
3401 conditionalExprs.append(expr)
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003402 else:
3403 if checkExpr:
3404 checkExpr += '\n' + indent
3405 checkExpr += expr
3406 #
3407 # If the struct is a function parameter (valuePrefix is empty) and only contains input-only parameters, it can be ignored if it is not an input
3408 elif (membersInputOnly == self.STRUCT_MEMBERS_INPUT_ONLY_NONE) or (not valuePrefix and membersInputOnly == self.STRUCT_MEMBERS_INPUT_ONLY_EXCLUSIVE and value.isconst):
3409 if checkExpr:
3410 checkExpr += '\n' + indent
3411 checkExpr += expr
3412 elif value.isbool and value.isconst:
3413 expr = 'skipCall |= validate_bool32_array(report_data, {}, {}, {pf}{}, {pf}{});\n'.format(name, valueDisplayName, lenParam.name, value.name, pf=valuePrefix)
3414 if checkExpr:
3415 checkExpr += '\n' + indent
3416 checkExpr += expr
3417 elif value.israngedenum and value.isconst:
3418 enumRange = self.enumRanges[value.type]
3419 expr = 'skipCall |= validate_ranged_enum_array(report_data, {}, {}, "{}", {}, {}, {pf}{}, {pf}{});\n'.format(name, valueDisplayName, value.type, enumRange[0], enumRange[1], lenParam.name, value.name, pf=valuePrefix)
3420 if checkExpr:
3421 checkExpr += '\n' + indent
3422 checkExpr += expr
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003423 elif value.type in self.validatedStructs:
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003424 # The name of the value with prefix applied
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003425 prefix = '(std::string({}) + std::string("{}.")).c_str()'.format(variablePrefix, value.name) if variablePrefix else '"{}."'.format(value.name)
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003426 #
3427 membersInputOnly = self.validatedStructs[value.type]
3428 #
3429 # If the current struct has mixed 'input-only' and 'non-input-only' members, it needs an isInput flag
3430 if membersInputOnly == self.STRUCT_MEMBERS_INPUT_ONLY_MIXED:
3431 # If this function is called from another struct validation function (valuePrefix is not empty), then we forward the 'isInput' prameter
3432 isInput = 'isInput'
3433 if not valuePrefix:
3434 # We are validating function parameters and need to determine if the current value is an input parameter
3435 isInput = 'true' if value.isconst else 'false'
3436 if checkExpr:
3437 checkExpr += '\n' + indent
3438 checkExpr += 'skipCall |= parameter_validation_{}(report_data, {}, {}, {}, &({}{}));\n'.format(value.type, name, prefix, isInput, valuePrefix, value.name)
3439 else:
3440 # Validation function does not have an isInput field
3441 expr = 'skipCall |= parameter_validation_{}(report_data, {}, {}, &({}{}));\n'.format(value.type, name, prefix, valuePrefix, value.name)
3442 #
3443 # If the struct only has input-only members and is a member of another struct, it is conditionally processed based on 'isInput'
3444 if valuePrefix and membersInputOnly == self.STRUCT_MEMBERS_INPUT_ONLY_EXCLUSIVE:
3445 if needConditionCheck:
3446 conditionalExprs.append(expr)
3447 else:
3448 if checkExpr:
3449 checkExpr += '\n' + indent
3450 checkExpr += expr
3451 #
3452 # If the struct is a function parameter (valuePrefix is empty) and only contains input-only parameters, it can be ignored if it is not an input
3453 elif (membersInputOnly == self.STRUCT_MEMBERS_INPUT_ONLY_NONE) or (not valuePrefix and membersInputOnly == self.STRUCT_MEMBERS_INPUT_ONLY_EXCLUSIVE and value.isconst):
3454 if checkExpr:
3455 checkExpr += '\n' + indent
3456 checkExpr += expr
3457 elif value.isbool:
3458 expr = 'skipCall |= validate_bool32(report_data, {}, {}, {}{});\n'.format(name, valueDisplayName, valuePrefix, value.name)
3459 if needConditionCheck:
3460 conditionalExprs.append(expr)
3461 else:
3462 checkExpr = expr
3463 elif value.israngedenum:
3464 enumRange = self.enumRanges[value.type]
3465 expr = 'skipCall |= validate_ranged_enum(report_data, {}, {}, "{}", {}, {}, {}{});\n'.format(name, valueDisplayName, value.type, enumRange[0], enumRange[1], valuePrefix, value.name)
3466 if needConditionCheck:
3467 conditionalExprs.append(expr)
3468 else:
3469 checkExpr = expr
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003470 #
3471 # Append the parameter check to the function body for the current command
3472 if checkExpr:
3473 funcBody += '\n'
3474 if lenParam and ('->' in lenParam.name):
3475 # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
3476 funcBody += self.genCheckedLengthCall(indent, lenParam.name, checkExpr)
3477 else:
3478 funcBody += indent + checkExpr
Dustin Graves80c0dea2016-03-03 14:17:08 -07003479 elif not value.iscount:
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003480 # If no expression was generated for this value, it is unreferenced by the validation function, unless
3481 # it is an array count, which is indirectly referenced for array valiadation.
Dustin Graves80c0dea2016-03-03 14:17:08 -07003482 unused.append(value.name)
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003483 # Add the 'input' only checks
3484 if conditionalExprs:
3485 funcBody += '\n'
3486 funcBody += indent + 'if (isInput) {'
3487 indent = self.incIndent(indent)
3488 for conditionalExpr in conditionalExprs:
3489 funcBody += '\n'
3490 funcBody += indent + conditionalExpr
3491 indent = self.decIndent(indent)
3492 funcBody += indent + '}\n'
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003493 return funcBody, unused
3494 #
3495 # Post-process the collected struct member data to create a list of structs
3496 # with members that need to be validated
3497 def prepareStructMemberData(self):
3498 for struct in self.structMembers:
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003499 inputOnly = False
3500 validated = False
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003501 for member in struct.members:
3502 if not member.iscount:
3503 lenParam = self.getLenParam(struct.members, member.len)
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003504 # The sType value needs to be validated
3505 # The pNext value needs to be validated
3506 # A required array/count needs to be validated
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003507 # A required pointer needs to be validated
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003508 # A bool needs to be validated, and the struct is an input parameter
3509 # An enum needs to be validated, and the struct is an input parameter
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003510 if member.type in self.structTypes:
3511 validated = True
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003512 elif member.name == 'pNext':
3513 validated = True
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003514 elif member.ispointer and lenParam: # This is an array
3515 # Make sure len is not optional
3516 if lenParam.ispointer:
3517 if not lenParam.isoptional[0] or not lenParam.isoptional[1] or not member.isoptional:
3518 validated = True
3519 else:
3520 if not lenParam.isoptional or not member.isoptional:
3521 validated = True
3522 elif member.ispointer and not member.isoptional:
3523 validated = True
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003524 elif member.isbool or member.israngedenum:
3525 inputOnly = True
3526 #
3527 if validated or inputOnly:
3528 if not validated:
3529 self.validatedStructs[struct.name] = self.STRUCT_MEMBERS_INPUT_ONLY_EXCLUSIVE
3530 elif not inputOnly:
3531 self.validatedStructs[struct.name] = self.STRUCT_MEMBERS_INPUT_ONLY_NONE
3532 else:
3533 self.validatedStructs[struct.name] = self.STRUCT_MEMBERS_INPUT_ONLY_MIXED
3534 # Second pass to check for struct members that are structs requiring validation
3535 # May not be necessary, as structs seem to always be defined before first use in the XML registry
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003536 for member in struct.members:
3537 if member.type in self.validatedStructs:
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003538 memberInputOnly = self.validatedStructs[member.type]
3539 if not struct.name in self.validatedStructs:
3540 self.validatedStructs[struct.name] = memberInputOnly
3541 elif self.validatedStructs[struct.name] != memberInputOnly:
3542 self.validatedStructs[struct.name] = self.STRUCT_MEMBERS_INPUT_ONLY_MIXED
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003543 #
3544 # Generate the struct member check code from the captured data
3545 def processStructMemberData(self):
3546 indent = self.incIndent(None)
3547 for struct in self.structMembers:
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003548 needConditionCheck = False
3549 if struct.name in self.validatedStructs and self.validatedStructs[struct.name] == self.STRUCT_MEMBERS_INPUT_ONLY_MIXED:
3550 needConditionCheck = True
3551 #
3552 # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
3553 funcBody, unused = self.genFuncBody(self.incIndent(indent), 'pFuncName', struct.members, 'pStruct->', 'pVariableName', struct.name, needConditionCheck)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003554 if funcBody:
Mark Lobodzinski1c333572016-03-17 15:08:18 -06003555 cmdDef = 'static VkBool32 parameter_validation_{}(\n'.format(struct.name)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003556 cmdDef += ' debug_report_data*'.ljust(self.genOpts.alignFuncParam) + ' report_data,\n'
3557 cmdDef += ' const char*'.ljust(self.genOpts.alignFuncParam) + ' pFuncName,\n'
3558 cmdDef += ' const char*'.ljust(self.genOpts.alignFuncParam) + ' pVariableName,\n'
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003559 # If there is a funcBody, this struct must have an entry in the validatedStructs dictionary
3560 if self.validatedStructs[struct.name] == self.STRUCT_MEMBERS_INPUT_ONLY_MIXED:
3561 # If the struct has mixed input only and non-input only members, it needs a flag to indicate if it is an input or output
3562 cmdDef += ' bool'.ljust(self.genOpts.alignFuncParam) + ' isInput,\n'
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003563 cmdDef += ' const {}*'.format(struct.name).ljust(self.genOpts.alignFuncParam) + ' pStruct)\n'
3564 cmdDef += '{\n'
3565 cmdDef += indent + 'VkBool32 skipCall = VK_FALSE;\n'
3566 cmdDef += '\n'
3567 cmdDef += indent + 'if (pStruct != NULL) {'
3568 cmdDef += funcBody
3569 cmdDef += indent +'}\n'
3570 cmdDef += '\n'
3571 cmdDef += indent + 'return skipCall;\n'
3572 cmdDef += '}\n'
3573 self.appendSection('command', cmdDef)
3574 #
3575 # Generate the command param check code from the captured data
3576 def processCmdData(self):
3577 indent = self.incIndent(None)
3578 for command in self.commands:
Dustin Gravesc3fc3d82016-03-23 19:44:00 -06003579 cmdBody, unused = self.genFuncBody(indent, '"{}"'.format(command.name), command.params, '', None, None, False)
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003580 if cmdBody:
3581 cmdDef = self.getCmdDef(command) + '\n'
3582 cmdDef += '{\n'
3583 # Process unused parameters
3584 # Ignore the first dispatch handle parameter, which is not
Mark Lobodzinski1c333572016-03-17 15:08:18 -06003585 # processed by parameter_validation (except for vkCreateInstance, which
Dustin Graves80c0dea2016-03-03 14:17:08 -07003586 # does not have a handle as its first parameter)
3587 startIndex = 1
3588 if command.name == 'vkCreateInstance':
3589 startIndex = 0
3590 for name in unused[startIndex:]:
Dustin Gravesaf0d6dc2016-03-02 18:23:29 -07003591 cmdDef += indent + 'UNUSED_PARAMETER({});\n'.format(name)
3592 if len(unused) > 1:
3593 cmdDef += '\n'
3594 cmdDef += indent + 'VkBool32 skipCall = VK_FALSE;\n'
3595 cmdDef += cmdBody
3596 cmdDef += '\n'
3597 cmdDef += indent + 'return skipCall;\n'
3598 cmdDef += '}\n'
3599 self.appendSection('command', cmdDef)