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