blob: 4328ed4d07e94b52361838a4286400cac876b9eb [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,
Jamie Madille6f08932016-05-04 08:17:33 -0700325 alignFuncParam = 0,
326 genDirectory = None):
Mike Stroyan845bdc42015-11-02 15:30:20 -0700327 GeneratorOptions.__init__(self, filename, apiname, profile,
328 versions, emitversions, defaultExtensions,
329 addExtensions, removeExtensions, sortProcedure)
330 self.prefixText = prefixText
331 self.genFuncPointers = genFuncPointers
332 self.protectFile = protectFile
333 self.protectFeature = protectFeature
334 self.protectProto = protectProto
335 self.protectProtoStr = protectProtoStr
336 self.apicall = apicall
337 self.apientry = apientry
338 self.apientryp = apientryp
339 self.indentFuncProto = indentFuncProto
340 self.indentFuncPointer = indentFuncPointer
341 self.alignFuncParam = alignFuncParam
Jamie Madille6f08932016-05-04 08:17:33 -0700342 self.genDirectory = genDirectory
Mike Stroyan845bdc42015-11-02 15:30:20 -0700343
344
Dustin Gravesdfa6acf2016-02-11 10:10:14 -0700345# ParamCheckerGeneratorOptions - subclass of GeneratorOptions.
346#
Mark Lobodzinski739391a2016-03-17 15:08:18 -0600347# Adds options used by ParamCheckerOutputGenerator objects during parameter validation
Dustin Gravesdfa6acf2016-02-11 10:10:14 -0700348# generation.
349#
350# Additional members
351# prefixText - list of strings to prefix generated header with
352# (usually a copyright statement + calling convention macros).
353# protectFile - True if multiple inclusion protection should be
354# generated (based on the filename) around the entire header.
355# protectFeature - True if #ifndef..#endif protection should be
356# generated around a feature interface in the header file.
357# genFuncPointers - True if function pointer typedefs should be
358# generated
359# protectProto - If conditional protection should be generated
360# around prototype declarations, set to either '#ifdef'
361# to require opt-in (#ifdef protectProtoStr) or '#ifndef'
362# to require opt-out (#ifndef protectProtoStr). Otherwise
363# set to None.
364# protectProtoStr - #ifdef/#ifndef symbol to use around prototype
365# declarations, if protectProto is set
366# apicall - string to use for the function declaration prefix,
367# such as APICALL on Windows.
368# apientry - string to use for the calling convention macro,
369# in typedefs, such as APIENTRY.
370# apientryp - string to use for the calling convention macro
371# in function pointer typedefs, such as APIENTRYP.
372# indentFuncProto - True if prototype declarations should put each
373# parameter on a separate line
374# indentFuncPointer - True if typedefed function pointers should put each
375# parameter on a separate line
376# alignFuncParam - if nonzero and parameters are being put on a
377# separate line, align parameter names at the specified column
378class ParamCheckerGeneratorOptions(GeneratorOptions):
379 """Represents options during C interface generation for headers"""
380 def __init__(self,
381 filename = None,
382 apiname = None,
383 profile = None,
384 versions = '.*',
385 emitversions = '.*',
386 defaultExtensions = None,
387 addExtensions = None,
388 removeExtensions = None,
389 sortProcedure = regSortFeatures,
390 prefixText = "",
391 genFuncPointers = True,
392 protectFile = True,
393 protectFeature = True,
394 protectProto = None,
395 protectProtoStr = None,
396 apicall = '',
397 apientry = '',
398 apientryp = '',
399 indentFuncProto = True,
400 indentFuncPointer = False,
Jamie Madille6f08932016-05-04 08:17:33 -0700401 alignFuncParam = 0,
402 genDirectory = None):
Dustin Gravesdfa6acf2016-02-11 10:10:14 -0700403 GeneratorOptions.__init__(self, filename, apiname, profile,
404 versions, emitversions, defaultExtensions,
405 addExtensions, removeExtensions, sortProcedure)
406 self.prefixText = prefixText
407 self.genFuncPointers = genFuncPointers
408 self.protectFile = protectFile
409 self.protectFeature = protectFeature
410 self.protectProto = protectProto
411 self.protectProtoStr = protectProtoStr
412 self.apicall = apicall
413 self.apientry = apientry
414 self.apientryp = apientryp
415 self.indentFuncProto = indentFuncProto
416 self.indentFuncPointer = indentFuncPointer
417 self.alignFuncParam = alignFuncParam
Jamie Madille6f08932016-05-04 08:17:33 -0700418 self.genDirectory = genDirectory
Dustin Gravesdfa6acf2016-02-11 10:10:14 -0700419
420
Mike Stroyandee76ef2016-01-07 15:35:37 -0700421# OutputGenerator - base class for generating API interfaces.
422# Manages basic logic, logging, and output file control
423# Derived classes actually generate formatted output.
424#
425# ---- methods ----
426# OutputGenerator(errFile, warnFile, diagFile)
427# errFile, warnFile, diagFile - file handles to write errors,
428# warnings, diagnostics to. May be None to not write.
429# logMsg(level, *args) - log messages of different categories
430# level - 'error', 'warn', or 'diag'. 'error' will also
431# raise a UserWarning exception
432# *args - print()-style arguments
433# setExtMap(map) - specify a dictionary map from extension names to
434# numbers, used in creating values for extension enumerants.
435# beginFile(genOpts) - start a new interface file
436# genOpts - GeneratorOptions controlling what's generated and how
437# endFile() - finish an interface file, closing it when done
438# beginFeature(interface, emit) - write interface for a feature
439# and tag generated features as having been done.
440# interface - element for the <version> / <extension> to generate
441# emit - actually write to the header only when True
442# endFeature() - finish an interface.
443# genType(typeinfo,name) - generate interface for a type
444# typeinfo - TypeInfo for a type
445# genStruct(typeinfo,name) - generate interface for a C "struct" type.
446# typeinfo - TypeInfo for a type interpreted as a struct
447# genGroup(groupinfo,name) - generate interface for a group of enums (C "enum")
448# groupinfo - GroupInfo for a group
449# genEnum(enuminfo, name) - generate interface for an enum (constant)
450# enuminfo - EnumInfo for an enum
451# name - enum name
452# genCmd(cmdinfo) - generate interface for a command
453# cmdinfo - CmdInfo for a command
454# makeCDecls(cmd) - return C prototype and function pointer typedef for a
455# <command> Element, as a list of two strings
456# cmd - Element for the <command>
457# newline() - print a newline to the output file (utility function)
458#
459class OutputGenerator:
460 """Generate specified API interfaces in a specific style, such as a C header"""
461 def __init__(self,
462 errFile = sys.stderr,
463 warnFile = sys.stderr,
464 diagFile = sys.stdout):
465 self.outFile = None
466 self.errFile = errFile
467 self.warnFile = warnFile
468 self.diagFile = diagFile
469 # Internal state
470 self.featureName = None
471 self.genOpts = None
472 self.registry = None
473 # Used for extension enum value generation
474 self.extBase = 1000000000
475 self.extBlockSize = 1000
476 #
477 # logMsg - write a message of different categories to different
478 # destinations.
479 # level -
480 # 'diag' (diagnostic, voluminous)
481 # 'warn' (warning)
482 # 'error' (fatal error - raises exception after logging)
483 # *args - print()-style arguments to direct to corresponding log
484 def logMsg(self, level, *args):
485 """Log a message at the given level. Can be ignored or log to a file"""
486 if (level == 'error'):
487 strfile = io.StringIO()
488 write('ERROR:', *args, file=strfile)
489 if (self.errFile != None):
490 write(strfile.getvalue(), file=self.errFile)
491 raise UserWarning(strfile.getvalue())
492 elif (level == 'warn'):
493 if (self.warnFile != None):
494 write('WARNING:', *args, file=self.warnFile)
495 elif (level == 'diag'):
496 if (self.diagFile != None):
497 write('DIAG:', *args, file=self.diagFile)
498 else:
499 raise UserWarning(
500 '*** FATAL ERROR in Generator.logMsg: unknown level:' + level)
501 #
502 # enumToValue - parses and converts an <enum> tag into a value.
503 # Returns a list
504 # first element - integer representation of the value, or None
505 # if needsNum is False. The value must be a legal number
506 # if needsNum is True.
507 # second element - string representation of the value
508 # There are several possible representations of values.
509 # A 'value' attribute simply contains the value.
510 # A 'bitpos' attribute defines a value by specifying the bit
511 # position which is set in that value.
512 # A 'offset','extbase','extends' triplet specifies a value
513 # as an offset to a base value defined by the specified
514 # 'extbase' extension name, which is then cast to the
515 # typename specified by 'extends'. This requires probing
516 # the registry database, and imbeds knowledge of the
517 # Vulkan extension enum scheme in this function.
518 def enumToValue(self, elem, needsNum):
519 name = elem.get('name')
520 numVal = None
521 if ('value' in elem.keys()):
522 value = elem.get('value')
523 # print('About to translate value =', value, 'type =', type(value))
524 if (needsNum):
525 numVal = int(value, 0)
526 # If there's a non-integer, numeric 'type' attribute (e.g. 'u' or
527 # 'ull'), append it to the string value.
528 # t = enuminfo.elem.get('type')
529 # if (t != None and t != '' and t != 'i' and t != 's'):
530 # value += enuminfo.type
531 self.logMsg('diag', 'Enum', name, '-> value [', numVal, ',', value, ']')
532 return [numVal, value]
533 if ('bitpos' in elem.keys()):
534 value = elem.get('bitpos')
535 numVal = int(value, 0)
536 numVal = 1 << numVal
537 value = '0x%08x' % numVal
538 self.logMsg('diag', 'Enum', name, '-> bitpos [', numVal, ',', value, ']')
539 return [numVal, value]
540 if ('offset' in elem.keys()):
541 # Obtain values in the mapping from the attributes
542 enumNegative = False
543 offset = int(elem.get('offset'),0)
544 extnumber = int(elem.get('extnumber'),0)
545 extends = elem.get('extends')
546 if ('dir' in elem.keys()):
547 enumNegative = True
548 self.logMsg('diag', 'Enum', name, 'offset =', offset,
549 'extnumber =', extnumber, 'extends =', extends,
550 'enumNegative =', enumNegative)
551 # Now determine the actual enumerant value, as defined
552 # in the "Layers and Extensions" appendix of the spec.
553 numVal = self.extBase + (extnumber - 1) * self.extBlockSize + offset
554 if (enumNegative):
555 numVal = -numVal
556 value = '%d' % numVal
557 # More logic needed!
558 self.logMsg('diag', 'Enum', name, '-> offset [', numVal, ',', value, ']')
559 return [numVal, value]
560 return [None, None]
561 #
562 def beginFile(self, genOpts):
563 self.genOpts = genOpts
564 #
565 # Open specified output file. Not done in constructor since a
566 # Generator can be used without writing to a file.
567 if (self.genOpts.filename != None):
Jamie Madille6f08932016-05-04 08:17:33 -0700568 if (self.genOpts.genDirectory != None):
569 self.outFile = open(os.path.join(self.genOpts.genDirectory, self.genOpts.filename), 'w')
570 else:
571 self.outFile = open(self.genOpts.filename, 'w')
Mike Stroyandee76ef2016-01-07 15:35:37 -0700572 else:
573 self.outFile = sys.stdout
574 def endFile(self):
575 self.errFile and self.errFile.flush()
576 self.warnFile and self.warnFile.flush()
577 self.diagFile and self.diagFile.flush()
578 self.outFile.flush()
579 if (self.outFile != sys.stdout and self.outFile != sys.stderr):
580 self.outFile.close()
581 self.genOpts = None
582 #
583 def beginFeature(self, interface, emit):
584 self.emit = emit
585 self.featureName = interface.get('name')
586 # If there's an additional 'protect' attribute in the feature, save it
587 self.featureExtraProtect = interface.get('protect')
588 def endFeature(self):
589 # Derived classes responsible for emitting feature
590 self.featureName = None
591 self.featureExtraProtect = None
592 # Utility method to validate we're generating something only inside a
593 # <feature> tag
594 def validateFeature(self, featureType, featureName):
595 if (self.featureName == None):
596 raise UserWarning('Attempt to generate', featureType, name,
597 'when not in feature')
598 #
599 # Type generation
600 def genType(self, typeinfo, name):
601 self.validateFeature('type', name)
602 #
603 # Struct (e.g. C "struct" type) generation
604 def genStruct(self, typeinfo, name):
605 self.validateFeature('struct', name)
606 #
607 # Group (e.g. C "enum" type) generation
608 def genGroup(self, groupinfo, name):
609 self.validateFeature('group', name)
610 #
611 # Enumerant (really, constant) generation
612 def genEnum(self, enuminfo, name):
613 self.validateFeature('enum', name)
614 #
615 # Command generation
616 def genCmd(self, cmd, name):
617 self.validateFeature('command', name)
618 #
619 # Utility functions - turn a <proto> <name> into C-language prototype
620 # and typedef declarations for that name.
621 # name - contents of <name> tag
622 # tail - whatever text follows that tag in the Element
623 def makeProtoName(self, name, tail):
624 return self.genOpts.apientry + name + tail
625 def makeTypedefName(self, name, tail):
626 return '(' + self.genOpts.apientryp + 'PFN_' + name + tail + ')'
627 #
628 # makeCParamDecl - return a string which is an indented, formatted
629 # declaration for a <param> or <member> block (e.g. function parameter
630 # or structure/union member).
631 # param - Element (<param> or <member>) to format
632 # aligncol - if non-zero, attempt to align the nested <name> element
633 # at this column
634 def makeCParamDecl(self, param, aligncol):
635 paramdecl = ' ' + noneStr(param.text)
636 for elem in param:
637 text = noneStr(elem.text)
638 tail = noneStr(elem.tail)
639 if (elem.tag == 'name' and aligncol > 0):
640 self.logMsg('diag', 'Aligning parameter', elem.text, 'to column', self.genOpts.alignFuncParam)
641 # Align at specified column, if possible
642 paramdecl = paramdecl.rstrip()
643 oldLen = len(paramdecl)
644 paramdecl = paramdecl.ljust(aligncol)
645 newLen = len(paramdecl)
646 self.logMsg('diag', 'Adjust length of parameter decl from', oldLen, 'to', newLen, ':', paramdecl)
647 paramdecl += text + tail
648 return paramdecl
649 #
650 # getCParamTypeLength - return the length of the type field is an indented, formatted
651 # declaration for a <param> or <member> block (e.g. function parameter
652 # or structure/union member).
653 # param - Element (<param> or <member>) to identify
654 def getCParamTypeLength(self, param):
655 paramdecl = ' ' + noneStr(param.text)
656 for elem in param:
657 text = noneStr(elem.text)
658 tail = noneStr(elem.tail)
659 if (elem.tag == 'name'):
660 # Align at specified column, if possible
661 newLen = len(paramdecl.rstrip())
662 self.logMsg('diag', 'Identifying length of', elem.text, 'as', newLen)
663 paramdecl += text + tail
664 return newLen
665 #
666 # makeCDecls - return C prototype and function pointer typedef for a
667 # command, as a two-element list of strings.
668 # cmd - Element containing a <command> tag
669 def makeCDecls(self, cmd):
670 """Generate C function pointer typedef for <command> Element"""
671 proto = cmd.find('proto')
672 params = cmd.findall('param')
673 # Begin accumulating prototype and typedef strings
674 pdecl = self.genOpts.apicall
675 tdecl = 'typedef '
676 #
677 # Insert the function return type/name.
678 # For prototypes, add APIENTRY macro before the name
679 # For typedefs, add (APIENTRY *<name>) around the name and
680 # use the PFN_cmdnameproc naming convention.
681 # Done by walking the tree for <proto> element by element.
Mike Stroyan3c5a6e22016-04-05 16:40:30 -0600682 # etree has elem.text followed by (elem[i], elem[i].tail)
Mike Stroyandee76ef2016-01-07 15:35:37 -0700683 # for each child element and any following text
684 # Leading text
685 pdecl += noneStr(proto.text)
686 tdecl += noneStr(proto.text)
687 # For each child element, if it's a <name> wrap in appropriate
688 # declaration. Otherwise append its contents and tail contents.
689 for elem in proto:
690 text = noneStr(elem.text)
691 tail = noneStr(elem.tail)
692 if (elem.tag == 'name'):
693 pdecl += self.makeProtoName(text, tail)
694 tdecl += self.makeTypedefName(text, tail)
695 else:
696 pdecl += text + tail
697 tdecl += text + tail
698 # Now add the parameter declaration list, which is identical
699 # for prototypes and typedefs. Concatenate all the text from
700 # a <param> node without the tags. No tree walking required
701 # since all tags are ignored.
702 # Uses: self.indentFuncProto
703 # self.indentFuncPointer
704 # self.alignFuncParam
705 # Might be able to doubly-nest the joins, e.g.
706 # ','.join(('_'.join([l[i] for i in range(0,len(l))])
707 n = len(params)
708 # Indented parameters
709 if n > 0:
710 indentdecl = '(\n'
711 for i in range(0,n):
712 paramdecl = self.makeCParamDecl(params[i], self.genOpts.alignFuncParam)
713 if (i < n - 1):
714 paramdecl += ',\n'
715 else:
716 paramdecl += ');'
717 indentdecl += paramdecl
718 else:
719 indentdecl = '(void);'
720 # Non-indented parameters
721 paramdecl = '('
722 if n > 0:
723 for i in range(0,n):
724 paramdecl += ''.join([t for t in params[i].itertext()])
725 if (i < n - 1):
726 paramdecl += ', '
727 else:
728 paramdecl += 'void'
729 paramdecl += ");";
730 return [ pdecl + indentdecl, tdecl + paramdecl ]
731 #
732 def newline(self):
733 write('', file=self.outFile)
734
735 def setRegistry(self, registry):
736 self.registry = registry
737 #
738
739# COutputGenerator - subclass of OutputGenerator.
740# Generates C-language API interfaces.
741#
742# ---- methods ----
743# COutputGenerator(errFile, warnFile, diagFile) - args as for
744# OutputGenerator. Defines additional internal state.
745# ---- methods overriding base class ----
746# beginFile(genOpts)
747# endFile()
748# beginFeature(interface, emit)
749# endFeature()
750# genType(typeinfo,name)
751# genStruct(typeinfo,name)
752# genGroup(groupinfo,name)
753# genEnum(enuminfo, name)
754# genCmd(cmdinfo)
755class COutputGenerator(OutputGenerator):
756 """Generate specified API interfaces in a specific style, such as a C header"""
757 # This is an ordered list of sections in the header file.
758 TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
759 'group', 'bitmask', 'funcpointer', 'struct']
760 ALL_SECTIONS = TYPE_SECTIONS + ['commandPointer', 'command']
761 def __init__(self,
762 errFile = sys.stderr,
763 warnFile = sys.stderr,
764 diagFile = sys.stdout):
765 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
766 # Internal state - accumulators for different inner block text
767 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
768 #
769 def beginFile(self, genOpts):
770 OutputGenerator.beginFile(self, genOpts)
771 # C-specific
772 #
773 # Multiple inclusion protection & C++ wrappers.
774 if (genOpts.protectFile and self.genOpts.filename):
Dustin Graves3ff520c2016-03-28 16:17:38 -0600775 headerSym = re.sub('\.h', '_h_',
776 os.path.basename(self.genOpts.filename)).upper()
Mike Stroyandee76ef2016-01-07 15:35:37 -0700777 write('#ifndef', headerSym, file=self.outFile)
778 write('#define', headerSym, '1', file=self.outFile)
779 self.newline()
780 write('#ifdef __cplusplus', file=self.outFile)
781 write('extern "C" {', file=self.outFile)
782 write('#endif', file=self.outFile)
783 self.newline()
784 #
785 # User-supplied prefix text, if any (list of strings)
786 if (genOpts.prefixText):
787 for s in genOpts.prefixText:
788 write(s, file=self.outFile)
789 #
790 # Some boilerplate describing what was generated - this
791 # will probably be removed later since the extensions
792 # pattern may be very long.
793 # write('/* Generated C header for:', file=self.outFile)
794 # write(' * API:', genOpts.apiname, file=self.outFile)
795 # if (genOpts.profile):
796 # write(' * Profile:', genOpts.profile, file=self.outFile)
797 # write(' * Versions considered:', genOpts.versions, file=self.outFile)
798 # write(' * Versions emitted:', genOpts.emitversions, file=self.outFile)
799 # write(' * Default extensions included:', genOpts.defaultExtensions, file=self.outFile)
800 # write(' * Additional extensions included:', genOpts.addExtensions, file=self.outFile)
801 # write(' * Extensions removed:', genOpts.removeExtensions, file=self.outFile)
802 # write(' */', file=self.outFile)
803 def endFile(self):
804 # C-specific
805 # Finish C++ wrapper and multiple inclusion protection
806 self.newline()
807 write('#ifdef __cplusplus', file=self.outFile)
808 write('}', file=self.outFile)
809 write('#endif', file=self.outFile)
810 if (self.genOpts.protectFile and self.genOpts.filename):
811 self.newline()
812 write('#endif', file=self.outFile)
813 # Finish processing in superclass
814 OutputGenerator.endFile(self)
815 def beginFeature(self, interface, emit):
816 # Start processing in superclass
817 OutputGenerator.beginFeature(self, interface, emit)
818 # C-specific
819 # Accumulate includes, defines, types, enums, function pointer typedefs,
820 # end function prototypes separately for this feature. They're only
821 # printed in endFeature().
822 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
823 def endFeature(self):
824 # C-specific
825 # Actually write the interface to the output file.
826 if (self.emit):
827 self.newline()
828 if (self.genOpts.protectFeature):
829 write('#ifndef', self.featureName, file=self.outFile)
830 # If type declarations are needed by other features based on
831 # this one, it may be necessary to suppress the ExtraProtect,
832 # or move it below the 'for section...' loop.
833 if (self.featureExtraProtect != None):
834 write('#ifdef', self.featureExtraProtect, file=self.outFile)
835 write('#define', self.featureName, '1', file=self.outFile)
836 for section in self.TYPE_SECTIONS:
837 contents = self.sections[section]
838 if contents:
839 write('\n'.join(contents), file=self.outFile)
840 self.newline()
841 if (self.genOpts.genFuncPointers and self.sections['commandPointer']):
842 write('\n'.join(self.sections['commandPointer']), file=self.outFile)
843 self.newline()
844 if (self.sections['command']):
845 if (self.genOpts.protectProto):
846 write(self.genOpts.protectProto,
847 self.genOpts.protectProtoStr, file=self.outFile)
848 write('\n'.join(self.sections['command']), end='', file=self.outFile)
849 if (self.genOpts.protectProto):
850 write('#endif', file=self.outFile)
851 else:
852 self.newline()
853 if (self.featureExtraProtect != None):
854 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
855 if (self.genOpts.protectFeature):
856 write('#endif /*', self.featureName, '*/', file=self.outFile)
857 # Finish processing in superclass
858 OutputGenerator.endFeature(self)
859 #
860 # Append a definition to the specified section
861 def appendSection(self, section, text):
862 # self.sections[section].append('SECTION: ' + section + '\n')
863 self.sections[section].append(text)
864 #
865 # Type generation
866 def genType(self, typeinfo, name):
867 OutputGenerator.genType(self, typeinfo, name)
868 typeElem = typeinfo.elem
869 # If the type is a struct type, traverse the imbedded <member> tags
870 # generating a structure. Otherwise, emit the tag text.
871 category = typeElem.get('category')
872 if (category == 'struct' or category == 'union'):
873 self.genStruct(typeinfo, name)
874 else:
875 # Replace <apientry /> tags with an APIENTRY-style string
876 # (from self.genOpts). Copy other text through unchanged.
877 # If the resulting text is an empty string, don't emit it.
878 s = noneStr(typeElem.text)
879 for elem in typeElem:
880 if (elem.tag == 'apientry'):
881 s += self.genOpts.apientry + noneStr(elem.tail)
882 else:
883 s += noneStr(elem.text) + noneStr(elem.tail)
884 if s:
885 # Add extra newline after multi-line entries.
886 if '\n' in s:
887 s += '\n'
888 self.appendSection(category, s)
889 #
890 # Struct (e.g. C "struct" type) generation.
891 # This is a special case of the <type> tag where the contents are
892 # interpreted as a set of <member> tags instead of freeform C
893 # C type declarations. The <member> tags are just like <param>
894 # tags - they are a declaration of a struct or union member.
895 # Only simple member declarations are supported (no nested
896 # structs etc.)
897 def genStruct(self, typeinfo, typeName):
898 OutputGenerator.genStruct(self, typeinfo, typeName)
899 body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
900 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
901 targetLen = 0;
902 for member in typeinfo.elem.findall('.//member'):
903 targetLen = max(targetLen, self.getCParamTypeLength(member))
904 for member in typeinfo.elem.findall('.//member'):
905 body += self.makeCParamDecl(member, targetLen + 4)
906 body += ';\n'
907 body += '} ' + typeName + ';\n'
908 self.appendSection('struct', body)
909 #
910 # Group (e.g. C "enum" type) generation.
911 # These are concatenated together with other types.
912 def genGroup(self, groupinfo, groupName):
913 OutputGenerator.genGroup(self, groupinfo, groupName)
914 groupElem = groupinfo.elem
Dustin Graves3ff520c2016-03-28 16:17:38 -0600915
916 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
917
918 expandPrefix = expandName
919 expandSuffix = ''
920 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
921 if expandSuffixMatch:
922 expandSuffix = '_' + expandSuffixMatch.group()
923 # Strip off the suffix from the prefix
924 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
925
Mike Stroyandee76ef2016-01-07 15:35:37 -0700926 # Prefix
927 body = "\ntypedef enum " + groupName + " {\n"
928
Dustin Graves3ff520c2016-03-28 16:17:38 -0600929 isEnum = ('FLAG_BITS' not in expandPrefix)
930
Mike Stroyandee76ef2016-01-07 15:35:37 -0700931 # Loop over the nested 'enum' tags. Keep track of the minimum and
932 # maximum numeric values, if they can be determined; but only for
933 # core API enumerants, not extension enumerants. This is inferred
934 # by looking for 'extends' attributes.
935 minName = None
936 for elem in groupElem.findall('enum'):
937 # Convert the value to an integer and use that to track min/max.
938 # Values of form -(number) are accepted but nothing more complex.
939 # Should catch exceptions here for more complex constructs. Not yet.
940 (numVal,strVal) = self.enumToValue(elem, True)
941 name = elem.get('name')
Dustin Graves3ff520c2016-03-28 16:17:38 -0600942
943 # Extension enumerants are only included if they are requested
944 # in addExtensions or match defaultExtensions.
945 if (elem.get('extname') is None or
946 re.match(self.genOpts.addExtensions,elem.get('extname')) is not None or
947 self.genOpts.defaultExtensions == elem.get('supported')):
948 body += " " + name + " = " + strVal + ",\n"
949
950 if (isEnum and elem.get('extends') is None):
Mike Stroyandee76ef2016-01-07 15:35:37 -0700951 if (minName == None):
952 minName = maxName = name
953 minValue = maxValue = numVal
954 elif (numVal < minValue):
955 minName = name
956 minValue = numVal
957 elif (numVal > maxValue):
958 maxName = name
959 maxValue = numVal
960 # Generate min/max value tokens and a range-padding enum. Need some
961 # additional padding to generate correct names...
Dustin Graves3ff520c2016-03-28 16:17:38 -0600962 if isEnum:
963 body += " " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n"
964 body += " " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n"
965 body += " " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n"
966
967 body += " " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
968
Mike Stroyandee76ef2016-01-07 15:35:37 -0700969 # Postfix
970 body += "} " + groupName + ";"
971 if groupElem.get('type') == 'bitmask':
972 section = 'bitmask'
973 else:
974 section = 'group'
975 self.appendSection(section, body)
976 # Enumerant generation
977 # <enum> tags may specify their values in several ways, but are usually
978 # just integers.
979 def genEnum(self, enuminfo, name):
980 OutputGenerator.genEnum(self, enuminfo, name)
981 (numVal,strVal) = self.enumToValue(enuminfo.elem, False)
982 body = '#define ' + name.ljust(33) + ' ' + strVal
983 self.appendSection('enum', body)
984 #
985 # Command generation
986 def genCmd(self, cmdinfo, name):
987 OutputGenerator.genCmd(self, cmdinfo, name)
988 #
989 decls = self.makeCDecls(cmdinfo.elem)
990 self.appendSection('command', decls[0] + '\n')
991 if (self.genOpts.genFuncPointers):
992 self.appendSection('commandPointer', decls[1])
993
994# DocOutputGenerator - subclass of OutputGenerator.
995# Generates AsciiDoc includes with C-language API interfaces, for reference
996# pages and the Vulkan specification. Similar to COutputGenerator, but
997# each interface is written into a different file as determined by the
998# options, only actual C types are emitted, and none of the boilerplate
999# preprocessor code is emitted.
1000#
1001# ---- methods ----
1002# DocOutputGenerator(errFile, warnFile, diagFile) - args as for
1003# OutputGenerator. Defines additional internal state.
1004# ---- methods overriding base class ----
1005# beginFile(genOpts)
1006# endFile()
1007# beginFeature(interface, emit)
1008# endFeature()
1009# genType(typeinfo,name)
1010# genStruct(typeinfo,name)
1011# genGroup(groupinfo,name)
1012# genEnum(enuminfo, name)
1013# genCmd(cmdinfo)
1014class DocOutputGenerator(OutputGenerator):
1015 """Generate specified API interfaces in a specific style, such as a C header"""
1016 def __init__(self,
1017 errFile = sys.stderr,
1018 warnFile = sys.stderr,
1019 diagFile = sys.stdout):
1020 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1021 #
1022 def beginFile(self, genOpts):
1023 OutputGenerator.beginFile(self, genOpts)
1024 def endFile(self):
1025 OutputGenerator.endFile(self)
1026 def beginFeature(self, interface, emit):
1027 # Start processing in superclass
1028 OutputGenerator.beginFeature(self, interface, emit)
1029 def endFeature(self):
1030 # Finish processing in superclass
1031 OutputGenerator.endFeature(self)
1032 #
1033 # Generate an include file
1034 #
1035 # directory - subdirectory to put file in
1036 # basename - base name of the file
1037 # contents - contents of the file (Asciidoc boilerplate aside)
1038 def writeInclude(self, directory, basename, contents):
1039 # Create file
1040 filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
1041 self.logMsg('diag', '# Generating include file:', filename)
1042 fp = open(filename, 'w')
1043 # Asciidoc anchor
Dustin Graves3ff520c2016-03-28 16:17:38 -06001044 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
1045 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001046 write('[[{0},{0}]]'.format(basename), file=fp)
1047 write('["source","{basebackend@docbook:c++:cpp}",title=""]', file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001048 write('endif::doctype-manpage[]', file=fp)
1049 write('ifdef::doctype-manpage[]', file=fp)
1050 write('["source","{basebackend@docbook:c++:cpp}"]', file=fp)
1051 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001052 write('------------------------------------------------------------------------------', file=fp)
1053 write(contents, file=fp)
1054 write('------------------------------------------------------------------------------', file=fp)
1055 fp.close()
1056 #
1057 # Type generation
1058 def genType(self, typeinfo, name):
1059 OutputGenerator.genType(self, typeinfo, name)
1060 typeElem = typeinfo.elem
1061 # If the type is a struct type, traverse the imbedded <member> tags
1062 # generating a structure. Otherwise, emit the tag text.
1063 category = typeElem.get('category')
1064 if (category == 'struct' or category == 'union'):
1065 self.genStruct(typeinfo, name)
1066 else:
1067 # Replace <apientry /> tags with an APIENTRY-style string
1068 # (from self.genOpts). Copy other text through unchanged.
1069 # If the resulting text is an empty string, don't emit it.
1070 s = noneStr(typeElem.text)
1071 for elem in typeElem:
1072 if (elem.tag == 'apientry'):
1073 s += self.genOpts.apientry + noneStr(elem.tail)
1074 else:
1075 s += noneStr(elem.text) + noneStr(elem.tail)
1076 if (len(s) > 0):
1077 if (category == 'bitmask'):
1078 self.writeInclude('flags', name, s + '\n')
1079 elif (category == 'enum'):
1080 self.writeInclude('enums', name, s + '\n')
1081 elif (category == 'funcpointer'):
1082 self.writeInclude('funcpointers', name, s+ '\n')
1083 else:
1084 self.logMsg('diag', '# NOT writing include file for type:',
1085 name, 'category: ', category)
1086 else:
1087 self.logMsg('diag', '# NOT writing empty include file for type', name)
1088 #
1089 # Struct (e.g. C "struct" type) generation.
1090 # This is a special case of the <type> tag where the contents are
1091 # interpreted as a set of <member> tags instead of freeform C
1092 # C type declarations. The <member> tags are just like <param>
1093 # tags - they are a declaration of a struct or union member.
1094 # Only simple member declarations are supported (no nested
1095 # structs etc.)
1096 def genStruct(self, typeinfo, typeName):
1097 OutputGenerator.genStruct(self, typeinfo, typeName)
1098 s = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
1099 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
1100 targetLen = 0;
1101 for member in typeinfo.elem.findall('.//member'):
1102 targetLen = max(targetLen, self.getCParamTypeLength(member))
1103 for member in typeinfo.elem.findall('.//member'):
1104 s += self.makeCParamDecl(member, targetLen + 4)
1105 s += ';\n'
1106 s += '} ' + typeName + ';'
1107 self.writeInclude('structs', typeName, s)
1108 #
1109 # Group (e.g. C "enum" type) generation.
1110 # These are concatenated together with other types.
1111 def genGroup(self, groupinfo, groupName):
1112 OutputGenerator.genGroup(self, groupinfo, groupName)
1113 groupElem = groupinfo.elem
Dustin Graves3ff520c2016-03-28 16:17:38 -06001114
1115 # See if we need min/max/num/padding at end
1116 expand = self.genOpts.expandEnumerants
1117
1118 if expand:
1119 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
1120 isEnum = ('FLAG_BITS' not in expandName)
1121
1122 expandPrefix = expandName
1123 expandSuffix = ''
1124
1125 # Look for a suffix
1126 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
1127 if expandSuffixMatch:
1128 expandSuffix = '_' + expandSuffixMatch.group()
1129 # Strip off the suffix from the prefix
1130 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
1131
Mike Stroyandee76ef2016-01-07 15:35:37 -07001132 # Prefix
1133 s = "typedef enum " + groupName + " {\n"
1134
1135 # Loop over the nested 'enum' tags. Keep track of the minimum and
1136 # maximum numeric values, if they can be determined.
1137 minName = None
1138 for elem in groupElem.findall('enum'):
1139 # Convert the value to an integer and use that to track min/max.
1140 # Values of form -(number) are accepted but nothing more complex.
1141 # Should catch exceptions here for more complex constructs. Not yet.
1142 (numVal,strVal) = self.enumToValue(elem, True)
1143 name = elem.get('name')
Dustin Graves3ff520c2016-03-28 16:17:38 -06001144
1145 # Extension enumerants are only included if they are requested
1146 # in addExtensions or match defaultExtensions.
1147 if (elem.get('extname') is None or
1148 re.match(self.genOpts.addExtensions,elem.get('extname')) is not None or
1149 self.genOpts.defaultExtensions == elem.get('supported')):
1150 s += " " + name + " = " + strVal + ",\n"
1151
1152 if (expand and isEnum and elem.get('extends') is None):
Mike Stroyandee76ef2016-01-07 15:35:37 -07001153 if (minName == None):
1154 minName = maxName = name
1155 minValue = maxValue = numVal
1156 elif (numVal < minValue):
1157 minName = name
1158 minValue = numVal
1159 elif (numVal > maxValue):
1160 maxName = name
1161 maxValue = numVal
1162 # Generate min/max value tokens and a range-padding enum. Need some
1163 # additional padding to generate correct names...
1164 if (expand):
1165 s += "\n"
Dustin Graves3ff520c2016-03-28 16:17:38 -06001166 if isEnum:
1167 s += " " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n"
1168 s += " " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n"
1169 s += " " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n"
1170
1171 s += " " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
Mike Stroyandee76ef2016-01-07 15:35:37 -07001172 # Postfix
1173 s += "} " + groupName + ";"
1174 self.writeInclude('enums', groupName, s)
1175 # Enumerant generation
1176 # <enum> tags may specify their values in several ways, but are usually
1177 # just integers.
1178 def genEnum(self, enuminfo, name):
1179 OutputGenerator.genEnum(self, enuminfo, name)
1180 (numVal,strVal) = self.enumToValue(enuminfo.elem, False)
1181 s = '#define ' + name.ljust(33) + ' ' + strVal
1182 self.logMsg('diag', '# NOT writing compile-time constant', name)
1183 # self.writeInclude('consts', name, s)
1184 #
1185 # Command generation
1186 def genCmd(self, cmdinfo, name):
1187 OutputGenerator.genCmd(self, cmdinfo, name)
1188 #
1189 decls = self.makeCDecls(cmdinfo.elem)
1190 self.writeInclude('protos', name, decls[0])
1191
1192# PyOutputGenerator - subclass of OutputGenerator.
1193# Generates Python data structures describing API names.
1194# Similar to DocOutputGenerator, but writes a single
1195# file.
1196#
1197# ---- methods ----
1198# PyOutputGenerator(errFile, warnFile, diagFile) - args as for
1199# OutputGenerator. Defines additional internal state.
1200# ---- methods overriding base class ----
1201# beginFile(genOpts)
1202# endFile()
1203# genType(typeinfo,name)
1204# genStruct(typeinfo,name)
1205# genGroup(groupinfo,name)
1206# genEnum(enuminfo, name)
1207# genCmd(cmdinfo)
1208class PyOutputGenerator(OutputGenerator):
1209 """Generate specified API interfaces in a specific style, such as a C header"""
1210 def __init__(self,
1211 errFile = sys.stderr,
1212 warnFile = sys.stderr,
1213 diagFile = sys.stdout):
1214 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1215 #
1216 def beginFile(self, genOpts):
1217 OutputGenerator.beginFile(self, genOpts)
1218 for dict in [ 'flags', 'enums', 'structs', 'consts', 'enums',
1219 'consts', 'protos', 'funcpointers' ]:
1220 write(dict, '= {}', file=self.outFile)
1221 def endFile(self):
1222 OutputGenerator.endFile(self)
1223 #
1224 # Add a name from the interface
1225 #
1226 # dict - type of name (see beginFile above)
1227 # name - name to add
1228 # value - A serializable Python value for the name
1229 def addName(self, dict, name, value=None):
1230 write(dict + "['" + name + "'] = ", value, file=self.outFile)
1231 #
1232 # Type generation
1233 # For 'struct' or 'union' types, defer to genStruct() to
1234 # add to the dictionary.
1235 # For 'bitmask' types, add the type name to the 'flags' dictionary,
1236 # with the value being the corresponding 'enums' name defining
1237 # the acceptable flag bits.
1238 # For 'enum' types, add the type name to the 'enums' dictionary,
1239 # with the value being '@STOPHERE@' (because this case seems
1240 # never to happen).
1241 # For 'funcpointer' types, add the type name to the 'funcpointers'
1242 # dictionary.
1243 # For 'handle' and 'define' types, add the handle or #define name
1244 # to the 'struct' dictionary, because that's how the spec sources
1245 # tag these types even though they aren't structs.
1246 def genType(self, typeinfo, name):
1247 OutputGenerator.genType(self, typeinfo, name)
1248 typeElem = typeinfo.elem
1249 # If the type is a struct type, traverse the imbedded <member> tags
1250 # generating a structure. Otherwise, emit the tag text.
1251 category = typeElem.get('category')
1252 if (category == 'struct' or category == 'union'):
1253 self.genStruct(typeinfo, name)
1254 else:
1255 # Extract the type name
1256 # (from self.genOpts). Copy other text through unchanged.
1257 # If the resulting text is an empty string, don't emit it.
1258 count = len(noneStr(typeElem.text))
1259 for elem in typeElem:
1260 count += len(noneStr(elem.text)) + len(noneStr(elem.tail))
1261 if (count > 0):
1262 if (category == 'bitmask'):
1263 requiredEnum = typeElem.get('requires')
1264 self.addName('flags', name, enquote(requiredEnum))
1265 elif (category == 'enum'):
1266 # This case never seems to come up!
1267 # @enums C 'enum' name Dictionary of enumerant names
1268 self.addName('enums', name, enquote('@STOPHERE@'))
1269 elif (category == 'funcpointer'):
1270 self.addName('funcpointers', name, None)
1271 elif (category == 'handle' or category == 'define'):
1272 self.addName('structs', name, None)
1273 else:
1274 write('# Unprocessed type:', name, 'category:', category, file=self.outFile)
1275 else:
1276 write('# Unprocessed type:', name, file=self.outFile)
1277 #
1278 # Struct (e.g. C "struct" type) generation.
1279 #
1280 # Add the struct name to the 'structs' dictionary, with the
1281 # value being an ordered list of the struct member names.
1282 def genStruct(self, typeinfo, typeName):
1283 OutputGenerator.genStruct(self, typeinfo, typeName)
1284
1285 members = [member.text for member in typeinfo.elem.findall('.//member/name')]
1286 self.addName('structs', typeName, members)
1287 #
1288 # Group (e.g. C "enum" type) generation.
1289 # These are concatenated together with other types.
1290 #
1291 # Add the enum type name to the 'enums' dictionary, with
1292 # the value being an ordered list of the enumerant names.
1293 # Add each enumerant name to the 'consts' dictionary, with
1294 # the value being the enum type the enumerant is part of.
1295 def genGroup(self, groupinfo, groupName):
1296 OutputGenerator.genGroup(self, groupinfo, groupName)
1297 groupElem = groupinfo.elem
1298
1299 # @enums C 'enum' name Dictionary of enumerant names
1300 # @consts C enumerant/const name Name of corresponding 'enums' key
1301
1302 # Loop over the nested 'enum' tags. Keep track of the minimum and
1303 # maximum numeric values, if they can be determined.
1304 enumerants = [elem.get('name') for elem in groupElem.findall('enum')]
1305 for name in enumerants:
1306 self.addName('consts', name, enquote(groupName))
1307 self.addName('enums', groupName, enumerants)
1308 # Enumerant generation (compile-time constants)
1309 #
1310 # Add the constant name to the 'consts' dictionary, with the
1311 # value being None to indicate that the constant isn't
1312 # an enumeration value.
1313 def genEnum(self, enuminfo, name):
1314 OutputGenerator.genEnum(self, enuminfo, name)
1315
1316 # @consts C enumerant/const name Name of corresponding 'enums' key
1317
1318 self.addName('consts', name, None)
1319 #
1320 # Command generation
1321 #
1322 # Add the command name to the 'protos' dictionary, with the
1323 # value being an ordered list of the parameter names.
1324 def genCmd(self, cmdinfo, name):
1325 OutputGenerator.genCmd(self, cmdinfo, name)
1326
1327 params = [param.text for param in cmdinfo.elem.findall('param/name')]
1328 self.addName('protos', name, params)
1329
1330# ValidityOutputGenerator - subclass of OutputGenerator.
1331# Generates AsciiDoc includes of valid usage information, for reference
1332# pages and the Vulkan specification. Similar to DocOutputGenerator.
1333#
1334# ---- methods ----
1335# ValidityOutputGenerator(errFile, warnFile, diagFile) - args as for
1336# OutputGenerator. Defines additional internal state.
1337# ---- methods overriding base class ----
1338# beginFile(genOpts)
1339# endFile()
1340# beginFeature(interface, emit)
1341# endFeature()
1342# genCmd(cmdinfo)
1343class ValidityOutputGenerator(OutputGenerator):
1344 """Generate specified API interfaces in a specific style, such as a C header"""
1345 def __init__(self,
1346 errFile = sys.stderr,
1347 warnFile = sys.stderr,
1348 diagFile = sys.stdout):
1349 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1350
1351 def beginFile(self, genOpts):
1352 OutputGenerator.beginFile(self, genOpts)
1353 def endFile(self):
1354 OutputGenerator.endFile(self)
1355 def beginFeature(self, interface, emit):
1356 # Start processing in superclass
1357 OutputGenerator.beginFeature(self, interface, emit)
1358 def endFeature(self):
1359 # Finish processing in superclass
1360 OutputGenerator.endFeature(self)
1361
1362 def makeParameterName(self, name):
1363 return 'pname:' + name
1364
1365 def makeStructName(self, name):
1366 return 'sname:' + name
1367
1368 def makeBaseTypeName(self, name):
1369 return 'basetype:' + name
1370
1371 def makeEnumerationName(self, name):
1372 return 'elink:' + name
1373
1374 def makeEnumerantName(self, name):
1375 return 'ename:' + name
1376
1377 def makeFLink(self, name):
1378 return 'flink:' + name
1379
1380 #
1381 # Generate an include file
1382 #
1383 # directory - subdirectory to put file in
1384 # basename - base name of the file
1385 # contents - contents of the file (Asciidoc boilerplate aside)
1386 def writeInclude(self, directory, basename, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes):
1387 # Create file
1388 filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
1389 self.logMsg('diag', '# Generating include file:', filename)
1390 fp = open(filename, 'w')
1391 # Asciidoc anchor
Dustin Graves3ff520c2016-03-28 16:17:38 -06001392 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001393
1394 # Valid Usage
1395 if validity is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001396 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001397 write('.Valid Usage', file=fp)
1398 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001399 write('endif::doctype-manpage[]', file=fp)
1400 write('ifdef::doctype-manpage[]', file=fp)
1401 write('Valid Usage', file=fp)
1402 write('-----------', file=fp)
1403 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001404 write(validity, file=fp, end='')
Dustin Graves3ff520c2016-03-28 16:17:38 -06001405 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001406 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001407 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001408 write('', file=fp)
1409
1410 # Host Synchronization
1411 if threadsafety is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001412 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001413 write('.Host Synchronization', file=fp)
1414 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001415 write('endif::doctype-manpage[]', file=fp)
1416 write('ifdef::doctype-manpage[]', file=fp)
1417 write('Host Synchronization', file=fp)
1418 write('--------------------', file=fp)
1419 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001420 write(threadsafety, file=fp, end='')
Dustin Graves3ff520c2016-03-28 16:17:38 -06001421 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001422 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001423 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001424 write('', file=fp)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07001425
Mike Stroyandee76ef2016-01-07 15:35:37 -07001426 # Command Properties - contained within a block, to avoid table numbering
1427 if commandpropertiesentry is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001428 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001429 write('.Command Properties', file=fp)
1430 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001431 write('endif::doctype-manpage[]', file=fp)
1432 write('ifdef::doctype-manpage[]', file=fp)
1433 write('Command Properties', file=fp)
1434 write('------------------', file=fp)
1435 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001436 write('[options="header", width="100%"]', file=fp)
1437 write('|=====================', file=fp)
1438 write('|Command Buffer Levels|Render Pass Scope|Supported Queue Types', file=fp)
1439 write(commandpropertiesentry, file=fp)
1440 write('|=====================', file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001441 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001442 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001443 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001444 write('', file=fp)
1445
1446 # Success Codes - contained within a block, to avoid table numbering
1447 if successcodes is not None or errorcodes is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001448 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001449 write('.Return Codes', file=fp)
1450 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001451 write('endif::doctype-manpage[]', file=fp)
1452 write('ifdef::doctype-manpage[]', file=fp)
1453 write('Return Codes', file=fp)
1454 write('------------', file=fp)
1455 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001456 if successcodes is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001457 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001458 write('<<fundamentals-successcodes,Success>>::', file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001459 write('endif::doctype-manpage[]', file=fp)
1460 write('ifdef::doctype-manpage[]', file=fp)
1461 write('On success, this command returns::', file=fp)
1462 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001463 write(successcodes, file=fp)
1464 if errorcodes is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001465 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001466 write('<<fundamentals-errorcodes,Failure>>::', file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001467 write('endif::doctype-manpage[]', file=fp)
1468 write('ifdef::doctype-manpage[]', file=fp)
1469 write('On failure, this command returns::', file=fp)
1470 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001471 write(errorcodes, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001472 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001473 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001474 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001475 write('', file=fp)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07001476
Mike Stroyandee76ef2016-01-07 15:35:37 -07001477 fp.close()
1478
1479 #
1480 # Check if the parameter passed in is a pointer
1481 def paramIsPointer(self, param):
1482 ispointer = False
1483 paramtype = param.find('type')
1484 if paramtype.tail is not None and '*' in paramtype.tail:
1485 ispointer = True
1486
1487 return ispointer
1488
1489 #
1490 # Check if the parameter passed in is a static array
1491 def paramIsStaticArray(self, param):
1492 if param.find('name').tail is not None:
1493 if param.find('name').tail[0] == '[':
1494 return True
1495
1496 #
1497 # Get the length of a parameter that's been identified as a static array
1498 def staticArrayLength(self, param):
1499 paramname = param.find('name')
1500 paramenumsize = param.find('enum')
1501
1502 if paramenumsize is not None:
1503 return paramenumsize.text
1504 else:
1505 return paramname.tail[1:-1]
1506
1507 #
1508 # Check if the parameter passed in is a pointer to an array
1509 def paramIsArray(self, param):
1510 return param.attrib.get('len') is not None
1511
1512 #
1513 # Get the parent of a handle object
1514 def getHandleParent(self, typename):
1515 types = self.registry.findall("types/type")
1516 for elem in types:
1517 if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
1518 return elem.attrib.get('parent')
1519
1520 #
1521 # Check if a parent object is dispatchable or not
1522 def isHandleTypeDispatchable(self, handlename):
1523 handle = self.registry.find("types/type/[name='" + handlename + "'][@category='handle']")
1524 if handle is not None and handle.find('type').text == 'VK_DEFINE_HANDLE':
1525 return True
1526 else:
1527 return False
1528
1529 def isHandleOptional(self, param, params):
1530
1531 # See if the handle is optional
1532 isOptional = False
1533
1534 # Simple, if it's optional, return true
1535 if param.attrib.get('optional') is not None:
1536 return True
1537
1538 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
1539 if param.attrib.get('noautovalidity') is not None:
1540 return True
1541
1542 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
1543 if self.paramIsArray(param):
1544 lengths = param.attrib.get('len').split(',')
1545 for length in lengths:
1546 if (length) != 'null-terminated' and (length) != '1':
1547 for otherparam in params:
1548 if otherparam.find('name').text == length:
1549 if otherparam.attrib.get('optional') is not None:
1550 return True
1551
1552 return False
1553 #
1554 # Get the category of a type
1555 def getTypeCategory(self, typename):
1556 types = self.registry.findall("types/type")
1557 for elem in types:
1558 if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
1559 return elem.attrib.get('category')
1560
1561 #
1562 # Make a chunk of text for the end of a parameter if it is an array
1563 def makeAsciiDocPreChunk(self, param, params):
1564 paramname = param.find('name')
1565 paramtype = param.find('type')
1566
1567 # General pre-amble. Check optionality and add stuff.
1568 asciidoc = '* '
1569
1570 if self.paramIsStaticArray(param):
1571 asciidoc += 'Any given element of '
1572
1573 elif self.paramIsArray(param):
1574 lengths = param.attrib.get('len').split(',')
1575
1576 # Find all the parameters that are called out as optional, so we can document that they might be zero, and the array may be ignored
1577 optionallengths = []
1578 for length in lengths:
1579 if (length) != 'null-terminated' and (length) != '1':
1580 for otherparam in params:
1581 if otherparam.find('name').text == length:
1582 if otherparam.attrib.get('optional') is not None:
1583 if self.paramIsPointer(otherparam):
1584 optionallengths.append('the value referenced by ' + self.makeParameterName(length))
1585 else:
1586 optionallengths.append(self.makeParameterName(length))
1587
1588 # Document that these arrays may be ignored if any of the length values are 0
1589 if len(optionallengths) != 0 or param.attrib.get('optional') is not None:
1590 asciidoc += 'If '
1591
1592
1593 if len(optionallengths) != 0:
1594 if len(optionallengths) == 1:
1595
1596 asciidoc += optionallengths[0]
1597 asciidoc += ' is '
1598
1599 else:
1600 asciidoc += ' or '.join(optionallengths)
1601 asciidoc += ' are '
1602
1603 asciidoc += 'not `0`, '
1604
1605 if len(optionallengths) != 0 and param.attrib.get('optional') is not None:
1606 asciidoc += 'and '
1607
1608 if param.attrib.get('optional') is not None:
1609 asciidoc += self.makeParameterName(paramname.text)
1610 asciidoc += ' is not `NULL`, '
1611
1612 elif param.attrib.get('optional') is not None:
1613 # Don't generate this stub for bitflags
1614 if self.getTypeCategory(paramtype.text) != 'bitmask':
1615 if param.attrib.get('optional').split(',')[0] == 'true':
1616 asciidoc += 'If '
1617 asciidoc += self.makeParameterName(paramname.text)
1618 asciidoc += ' is not '
1619 if self.paramIsArray(param) or self.paramIsPointer(param) or self.isHandleTypeDispatchable(paramtype.text):
1620 asciidoc += '`NULL`'
1621 elif self.getTypeCategory(paramtype.text) == 'handle':
1622 asciidoc += 'sname:VK_NULL_HANDLE'
1623 else:
1624 asciidoc += '`0`'
1625
1626 asciidoc += ', '
1627
1628 return asciidoc
1629
1630 #
1631 # Make the generic asciidoc line chunk portion used for all parameters.
1632 # May return an empty string if nothing to validate.
1633 def createValidationLineForParameterIntroChunk(self, param, params, typetext):
1634 asciidoc = ''
1635 paramname = param.find('name')
1636 paramtype = param.find('type')
1637
1638 asciidoc += self.makeAsciiDocPreChunk(param, params)
1639
1640 asciidoc += self.makeParameterName(paramname.text)
1641 asciidoc += ' must: be '
1642
1643 if self.paramIsArray(param):
1644 # Arrays. These are hard to get right, apparently
1645
1646 lengths = param.attrib.get('len').split(',')
1647
1648 if (lengths[0]) == 'null-terminated':
1649 asciidoc += 'a null-terminated '
1650 elif (lengths[0]) == '1':
1651 asciidoc += 'a pointer to '
1652 else:
1653 asciidoc += 'a pointer to an array of '
1654
1655 # Handle equations, which are currently denoted with latex
1656 if 'latexmath:' in lengths[0]:
1657 asciidoc += lengths[0]
1658 else:
1659 asciidoc += self.makeParameterName(lengths[0])
1660 asciidoc += ' '
1661
1662 for length in lengths[1:]:
1663 if (length) == 'null-terminated': # This should always be the last thing. If it ever isn't for some bizarre reason, then this will need some massaging.
1664 asciidoc += 'null-terminated '
1665 elif (length) == '1':
1666 asciidoc += 'pointers to '
1667 else:
1668 asciidoc += 'pointers to arrays of '
1669 # Handle equations, which are currently denoted with latex
1670 if 'latex:' in length:
1671 asciidoc += length
1672 else:
1673 asciidoc += self.makeParameterName(length)
1674 asciidoc += ' '
1675
1676 # Void pointers don't actually point at anything - remove the word "to"
1677 if paramtype.text == 'void':
1678 if lengths[-1] == '1':
1679 if len(lengths) > 1:
1680 asciidoc = asciidoc[:-5] # Take care of the extra s added by the post array chunk function. #HACK#
1681 else:
1682 asciidoc = asciidoc[:-4]
1683 else:
1684 # An array of void values is a byte array.
1685 asciidoc += 'byte'
1686
1687 elif paramtype.text == 'char':
1688 # A null terminated array of chars is a string
1689 if lengths[-1] == 'null-terminated':
1690 asciidoc += 'string'
1691 else:
1692 # Else it's just a bunch of chars
1693 asciidoc += 'char value'
1694 elif param.text is not None:
1695 # If a value is "const" that means it won't get modified, so it must be valid going into the function.
1696 if 'const' in param.text:
1697 typecategory = self.getTypeCategory(paramtype.text)
1698 if (typecategory != 'struct' and typecategory != 'union' and typecategory != 'basetype' and typecategory is not None) or not self.isStructAlwaysValid(paramtype.text):
1699 asciidoc += 'valid '
1700
1701 asciidoc += typetext
1702
1703 # pluralize
1704 if len(lengths) > 1 or (lengths[0] != '1' and lengths[0] != 'null-terminated'):
1705 asciidoc += 's'
1706
1707 elif self.paramIsPointer(param):
1708 # Handle pointers - which are really special case arrays (i.e. they don't have a length)
1709 pointercount = paramtype.tail.count('*')
1710
1711 # Could be multi-level pointers (e.g. ppData - pointer to a pointer). Handle that.
1712 for i in range(0, pointercount):
1713 asciidoc += 'a pointer to '
1714
1715 if paramtype.text == 'void':
1716 # If there's only one pointer, it's optional, and it doesn't point at anything in particular - we don't need any language.
1717 if pointercount == 1 and param.attrib.get('optional') is not None:
1718 return '' # early return
1719 else:
1720 # Pointer to nothing in particular - delete the " to " portion
1721 asciidoc = asciidoc[:-4]
1722 else:
1723 # Add an article for English semantic win
1724 asciidoc += 'a '
1725
1726 # If a value is "const" that means it won't get modified, so it must be valid going into the function.
1727 if param.text is not None and paramtype.text != 'void':
1728 if 'const' in param.text:
1729 asciidoc += 'valid '
1730
1731 asciidoc += typetext
1732
1733 else:
1734 # Non-pointer, non-optional things must be valid
1735 asciidoc += 'a valid '
1736 asciidoc += typetext
1737
1738 if asciidoc != '':
1739 asciidoc += '\n'
1740
1741 # Add additional line for non-optional bitmasks
1742 if self.getTypeCategory(paramtype.text) == 'bitmask':
1743 if param.attrib.get('optional') is None:
1744 asciidoc += '* '
1745 if self.paramIsArray(param):
1746 asciidoc += 'Each element of '
1747 asciidoc += 'pname:'
1748 asciidoc += paramname.text
1749 asciidoc += ' mustnot: be `0`'
1750 asciidoc += '\n'
1751
1752 return asciidoc
1753
1754 def makeAsciiDocLineForParameter(self, param, params, typetext):
1755 if param.attrib.get('noautovalidity') is not None:
1756 return ''
1757 asciidoc = self.createValidationLineForParameterIntroChunk(param, params, typetext)
1758
1759 return asciidoc
1760
1761 # Try to do check if a structure is always considered valid (i.e. there's no rules to its acceptance)
1762 def isStructAlwaysValid(self, structname):
1763
1764 struct = self.registry.find("types/type[@name='" + structname + "']")
1765
1766 params = struct.findall('member')
1767 validity = struct.find('validity')
1768
1769 if validity is not None:
1770 return False
1771
1772 for param in params:
1773 paramname = param.find('name')
1774 paramtype = param.find('type')
1775 typecategory = self.getTypeCategory(paramtype.text)
1776
1777 if paramname.text == 'pNext':
1778 return False
1779
1780 if paramname.text == 'sType':
1781 return False
1782
1783 if paramtype.text == 'void' or paramtype.text == 'char' or self.paramIsArray(param) or self.paramIsPointer(param):
1784 if self.makeAsciiDocLineForParameter(param, params, '') != '':
1785 return False
1786 elif typecategory == 'handle' or typecategory == 'enum' or typecategory == 'bitmask' or param.attrib.get('returnedonly') == 'true':
1787 return False
1788 elif typecategory == 'struct' or typecategory == 'union':
1789 if self.isStructAlwaysValid(paramtype.text) is False:
1790 return False
1791
1792 return True
1793
1794 #
1795 # Make an entire asciidoc line for a given parameter
1796 def createValidationLineForParameter(self, param, params, typecategory):
1797 asciidoc = ''
1798 paramname = param.find('name')
1799 paramtype = param.find('type')
1800
1801 if paramtype.text == 'void' or paramtype.text == 'char':
1802 # Chars and void are special cases - needs care inside the generator functions
1803 # A null-terminated char array is a string, else it's chars.
1804 # An array of void values is a byte array, a void pointer is just a pointer to nothing in particular
1805 asciidoc += self.makeAsciiDocLineForParameter(param, params, '')
1806 elif typecategory == 'bitmask':
1807 bitsname = paramtype.text.replace('Flags', 'FlagBits')
1808 if self.registry.find("enums[@name='" + bitsname + "']") is None:
1809 asciidoc += '* '
1810 asciidoc += self.makeParameterName(paramname.text)
1811 asciidoc += ' must: be `0`'
1812 asciidoc += '\n'
1813 else:
1814 if self.paramIsArray(param):
1815 asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combinations of ' + self.makeEnumerationName(bitsname) + ' value')
1816 else:
1817 asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combination of ' + self.makeEnumerationName(bitsname) + ' values')
1818 elif typecategory == 'handle':
1819 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' handle')
1820 elif typecategory == 'enum':
1821 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeEnumerationName(paramtype.text) + ' value')
1822 elif typecategory == 'struct':
1823 if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text):
1824 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' structure')
1825 elif typecategory == 'union':
1826 if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text):
1827 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' union')
1828 elif self.paramIsArray(param) or self.paramIsPointer(param):
1829 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeBaseTypeName(paramtype.text) + ' value')
1830
1831 return asciidoc
1832
1833 #
1834 # Make an asciidoc validity entry for a handle's parent object
1835 def makeAsciiDocHandleParent(self, param, params):
1836 asciidoc = ''
1837 paramname = param.find('name')
1838 paramtype = param.find('type')
1839
1840 # Deal with handle parents
1841 handleparent = self.getHandleParent(paramtype.text)
1842 if handleparent is not None:
1843 parentreference = None
1844 for otherparam in params:
1845 if otherparam.find('type').text == handleparent:
1846 parentreference = otherparam.find('name').text
1847 if parentreference is not None:
1848 asciidoc += '* '
1849
1850 if self.isHandleOptional(param, params):
1851 if self.paramIsArray(param):
1852 asciidoc += 'Each element of '
1853 asciidoc += self.makeParameterName(paramname.text)
1854 asciidoc += ' that is a valid handle'
1855 else:
1856 asciidoc += 'If '
1857 asciidoc += self.makeParameterName(paramname.text)
1858 asciidoc += ' is a valid handle, it'
1859 else:
1860 if self.paramIsArray(param):
1861 asciidoc += 'Each element of '
1862 asciidoc += self.makeParameterName(paramname.text)
1863 asciidoc += ' must: have been created, allocated or retrieved from '
1864 asciidoc += self.makeParameterName(parentreference)
1865
1866 asciidoc += '\n'
1867 return asciidoc
1868
1869 #
1870 # Generate an asciidoc validity line for the sType value of a struct
1871 def makeStructureType(self, blockname, param):
1872 asciidoc = '* '
1873 paramname = param.find('name')
1874 paramtype = param.find('type')
1875
1876 asciidoc += self.makeParameterName(paramname.text)
1877 asciidoc += ' must: be '
1878
1879 structuretype = ''
1880 for elem in re.findall(r'(([A-Z][a-z]+)|([A-Z][A-Z]+))', blockname):
1881 if elem[0] == 'Vk':
1882 structuretype += 'VK_STRUCTURE_TYPE_'
1883 else:
1884 structuretype += elem[0].upper()
1885 structuretype += '_'
1886
1887 asciidoc += self.makeEnumerantName(structuretype[:-1])
1888 asciidoc += '\n'
1889
1890 return asciidoc
1891
1892 #
1893 # Generate an asciidoc validity line for the pNext value of a struct
1894 def makeStructureExtensionPointer(self, param):
1895 asciidoc = '* '
1896 paramname = param.find('name')
1897 paramtype = param.find('type')
1898
1899 asciidoc += self.makeParameterName(paramname.text)
1900
1901 validextensionstructs = param.attrib.get('validextensionstructs')
1902 if validextensionstructs is None:
1903 asciidoc += ' must: be `NULL`'
1904 else:
1905 extensionstructs = validextensionstructs.split(',')
1906 asciidoc += ' must: point to one of ' + extensionstructs[:-1].join(', ') + ' or ' + extensionstructs[-1] + 'if the extension that introduced them is enabled '
1907
1908 asciidoc += '\n'
1909
1910 return asciidoc
1911
1912 #
1913 # Generate all the valid usage information for a given struct or command
1914 def makeValidUsageStatements(self, cmd, blockname, params, usages):
1915 # Start the asciidoc block for this
1916 asciidoc = ''
1917
1918 handles = []
1919 anyparentedhandlesoptional = False
1920 parentdictionary = {}
1921 arraylengths = set()
1922 for param in params:
1923 paramname = param.find('name')
1924 paramtype = param.find('type')
1925
1926 # Get the type's category
1927 typecategory = self.getTypeCategory(paramtype.text)
1928
1929 # Generate language to independently validate a parameter
1930 if paramtype.text == 'VkStructureType' and paramname.text == 'sType':
1931 asciidoc += self.makeStructureType(blockname, param)
1932 elif paramtype.text == 'void' and paramname.text == 'pNext':
1933 asciidoc += self.makeStructureExtensionPointer(param)
1934 else:
1935 asciidoc += self.createValidationLineForParameter(param, params, typecategory)
1936
1937 # Ensure that any parenting is properly validated, and list that a handle was found
1938 if typecategory == 'handle':
1939 # Don't detect a parent for return values!
1940 if not self.paramIsPointer(param) or (param.text is not None and 'const' in param.text):
1941 parent = self.getHandleParent(paramtype.text)
1942 if parent is not None:
1943 handles.append(param)
1944
1945 # If any param is optional, it affects the output
1946 if self.isHandleOptional(param, params):
1947 anyparentedhandlesoptional = True
1948
1949 # Find the first dispatchable parent
1950 ancestor = parent
1951 while ancestor is not None and not self.isHandleTypeDispatchable(ancestor):
1952 ancestor = self.getHandleParent(ancestor)
1953
1954 # If one was found, add this parameter to the parent dictionary
1955 if ancestor is not None:
1956 if ancestor not in parentdictionary:
1957 parentdictionary[ancestor] = []
1958
1959 if self.paramIsArray(param):
1960 parentdictionary[ancestor].append('the elements of ' + self.makeParameterName(paramname.text))
1961 else:
1962 parentdictionary[ancestor].append(self.makeParameterName(paramname.text))
1963
1964 # Get the array length for this parameter
1965 arraylength = param.attrib.get('len')
1966 if arraylength is not None:
1967 for onelength in arraylength.split(','):
1968 arraylengths.add(onelength)
1969
1970 # For any vkQueue* functions, there might be queue type data
1971 if 'vkQueue' in blockname:
1972 # The queue type must be valid
1973 queuetypes = cmd.attrib.get('queues')
1974 if queuetypes is not None:
1975 queuebits = []
1976 for queuetype in re.findall(r'([^,]+)', queuetypes):
1977 queuebits.append(queuetype.replace('_',' '))
1978
1979 asciidoc += '* '
1980 asciidoc += 'The pname:queue must: support '
1981 if len(queuebits) == 1:
1982 asciidoc += queuebits[0]
1983 else:
1984 asciidoc += (', ').join(queuebits[:-1])
1985 asciidoc += ' or '
1986 asciidoc += queuebits[-1]
1987 asciidoc += ' operations'
1988 asciidoc += '\n'
1989
1990 if 'vkCmd' in blockname:
1991 # The commandBuffer parameter must be being recorded
1992 asciidoc += '* '
1993 asciidoc += 'pname:commandBuffer must: be in the recording state'
1994 asciidoc += '\n'
1995
1996 # The queue type must be valid
1997 queuetypes = cmd.attrib.get('queues')
1998 queuebits = []
1999 for queuetype in re.findall(r'([^,]+)', queuetypes):
2000 queuebits.append(queuetype.replace('_',' '))
2001
2002 asciidoc += '* '
2003 asciidoc += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support '
2004 if len(queuebits) == 1:
2005 asciidoc += queuebits[0]
2006 else:
2007 asciidoc += (', ').join(queuebits[:-1])
2008 asciidoc += ' or '
2009 asciidoc += queuebits[-1]
2010 asciidoc += ' operations'
2011 asciidoc += '\n'
2012
2013 # Must be called inside/outside a renderpass appropriately
2014 renderpass = cmd.attrib.get('renderpass')
2015
2016 if renderpass != 'both':
2017 asciidoc += '* This command must: only be called '
2018 asciidoc += renderpass
2019 asciidoc += ' of a render pass instance'
2020 asciidoc += '\n'
2021
2022 # Must be in the right level command buffer
2023 cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
2024
2025 if cmdbufferlevel != 'primary,secondary':
2026 asciidoc += '* pname:commandBuffer must: be a '
2027 asciidoc += cmdbufferlevel
2028 asciidoc += ' sname:VkCommandBuffer'
2029 asciidoc += '\n'
2030
2031 # Any non-optional arraylengths should specify they must be greater than 0
2032 for param in params:
2033 paramname = param.find('name')
2034
2035 for arraylength in arraylengths:
2036 if paramname.text == arraylength and param.attrib.get('optional') is None:
2037 # Get all the array dependencies
2038 arrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
2039
2040 # Get all the optional array dependencies, including those not generating validity for some reason
2041 optionalarrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
2042 optionalarrays.extend(cmd.findall("param/[@len='" + arraylength + "'][@noautovalidity='true']"))
2043
2044 asciidoc += '* '
2045
2046 # Allow lengths to be arbitrary if all their dependents are optional
2047 if len(optionalarrays) == len(arrays) and len(optionalarrays) != 0:
2048 asciidoc += 'If '
2049 if len(optionalarrays) > 1:
2050 asciidoc += 'any of '
2051
2052 for array in optionalarrays[:-1]:
2053 asciidoc += self.makeParameterName(optionalarrays.find('name').text)
2054 asciidoc += ', '
2055
2056 if len(optionalarrays) > 1:
2057 asciidoc += 'and '
2058 asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
2059 asciidoc += ' are '
2060 else:
2061 asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
2062 asciidoc += ' is '
2063
2064 asciidoc += 'not `NULL`, '
2065
2066 if self.paramIsPointer(param):
2067 asciidoc += 'the value referenced by '
Mike Stroyandee76ef2016-01-07 15:35:37 -07002068
2069 elif self.paramIsPointer(param):
2070 asciidoc += 'The value referenced by '
Mike Stroyandee76ef2016-01-07 15:35:37 -07002071
2072 asciidoc += self.makeParameterName(arraylength)
2073 asciidoc += ' must: be greater than `0`'
2074 asciidoc += '\n'
2075
2076 # Find the parents of all objects referenced in this command
2077 for param in handles:
2078 asciidoc += self.makeAsciiDocHandleParent(param, params)
2079
2080 # Find the common ancestors of objects
2081 noancestorscount = 0
2082 while noancestorscount < len(parentdictionary):
2083 noancestorscount = 0
2084 oldparentdictionary = parentdictionary.copy()
2085 for parent in oldparentdictionary.items():
2086 ancestor = self.getHandleParent(parent[0])
2087
2088 while ancestor is not None and ancestor not in parentdictionary:
2089 ancestor = self.getHandleParent(ancestor)
2090
2091 if ancestor is not None:
2092 parentdictionary[ancestor] += parentdictionary.pop(parent[0])
2093 else:
2094 # No ancestors possible - so count it up
2095 noancestorscount += 1
2096
2097 # Add validation language about common ancestors
2098 for parent in parentdictionary.items():
2099 if len(parent[1]) > 1:
2100 parentlanguage = '* '
2101
2102 parentlanguage += 'Each of '
2103 parentlanguage += ", ".join(parent[1][:-1])
2104 parentlanguage += ' and '
2105 parentlanguage += parent[1][-1]
2106 if anyparentedhandlesoptional is True:
2107 parentlanguage += ' that are valid handles'
2108 parentlanguage += ' must: have been created, allocated or retrieved from the same '
2109 parentlanguage += self.makeStructName(parent[0])
2110 parentlanguage += '\n'
2111
2112 # Capitalize and add to the main language
2113 asciidoc += parentlanguage
2114
Dustin Graves3ff520c2016-03-28 16:17:38 -06002115 # Add in any plain-text validation language that should be added
Mike Stroyandee76ef2016-01-07 15:35:37 -07002116 for usage in usages:
2117 asciidoc += '* '
Dustin Graves3ff520c2016-03-28 16:17:38 -06002118 asciidoc += usage
Mike Stroyandee76ef2016-01-07 15:35:37 -07002119 asciidoc += '\n'
2120
2121 # In case there's nothing to report, return None
2122 if asciidoc == '':
2123 return None
2124 # Delimit the asciidoc block
2125 return asciidoc
2126
2127 def makeThreadSafetyBlock(self, cmd, paramtext):
2128 """Generate C function pointer typedef for <command> Element"""
2129 paramdecl = ''
2130
2131 # For any vkCmd* functions, the commandBuffer parameter must be being recorded
2132 if cmd.find('proto/name') is not None and 'vkCmd' in cmd.find('proto/name'):
2133 paramdecl += '* '
2134 paramdecl += 'The sname:VkCommandPool that pname:commandBuffer was created from'
2135 paramdecl += '\n'
2136
2137 # Find and add any parameters that are thread unsafe
2138 explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
2139 if (explicitexternsyncparams is not None):
2140 for param in explicitexternsyncparams:
2141 externsyncattribs = param.attrib.get('externsync')
2142 paramname = param.find('name')
2143 for externsyncattrib in externsyncattribs.split(','):
2144 paramdecl += '* '
2145 paramdecl += 'Host access to '
2146 if externsyncattrib == 'true':
2147 if self.paramIsArray(param):
2148 paramdecl += 'each member of ' + self.makeParameterName(paramname.text)
2149 elif self.paramIsPointer(param):
2150 paramdecl += 'the object referenced by ' + self.makeParameterName(paramname.text)
2151 else:
2152 paramdecl += self.makeParameterName(paramname.text)
2153 else:
2154 paramdecl += 'pname:'
2155 paramdecl += externsyncattrib
2156 paramdecl += ' must: be externally synchronized\n'
2157
2158 # Find and add any "implicit" parameters that are thread unsafe
2159 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2160 if (implicitexternsyncparams is not None):
2161 for elem in implicitexternsyncparams:
2162 paramdecl += '* '
2163 paramdecl += 'Host access to '
2164 paramdecl += elem.text
2165 paramdecl += ' must: be externally synchronized\n'
2166
2167 if (paramdecl == ''):
2168 return None
2169 else:
2170 return paramdecl
2171
2172 def makeCommandPropertiesTableEntry(self, cmd, name):
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002173
Mike Stroyandee76ef2016-01-07 15:35:37 -07002174 if 'vkCmd' in name:
2175 # Must be called inside/outside a renderpass appropriately
2176 cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
2177 cmdbufferlevel = (' + \n').join(cmdbufferlevel.title().split(','))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002178
Mike Stroyandee76ef2016-01-07 15:35:37 -07002179 renderpass = cmd.attrib.get('renderpass')
2180 renderpass = renderpass.capitalize()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002181
Mike Stroyandee76ef2016-01-07 15:35:37 -07002182 queues = cmd.attrib.get('queues')
2183 queues = (' + \n').join(queues.upper().split(','))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002184
2185 return '|' + cmdbufferlevel + '|' + renderpass + '|' + queues
Mike Stroyandee76ef2016-01-07 15:35:37 -07002186 elif 'vkQueue' in name:
2187 # Must be called inside/outside a renderpass appropriately
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002188
Mike Stroyandee76ef2016-01-07 15:35:37 -07002189 queues = cmd.attrib.get('queues')
2190 if queues is None:
2191 queues = 'Any'
2192 else:
2193 queues = (' + \n').join(queues.upper().split(','))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002194
2195 return '|-|-|' + queues
Mike Stroyandee76ef2016-01-07 15:35:37 -07002196
2197 return None
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002198
Mike Stroyandee76ef2016-01-07 15:35:37 -07002199 def makeSuccessCodes(self, cmd, name):
2200
2201 successcodes = cmd.attrib.get('successcodes')
2202 if successcodes is not None:
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002203
Mike Stroyandee76ef2016-01-07 15:35:37 -07002204 successcodeentry = ''
2205 successcodes = successcodes.split(',')
Dustin Graves3ff520c2016-03-28 16:17:38 -06002206 return '* ename:' + '\n* ename:'.join(successcodes)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002207
2208 return None
2209
2210 def makeErrorCodes(self, cmd, name):
2211
2212 errorcodes = cmd.attrib.get('errorcodes')
2213 if errorcodes is not None:
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002214
Mike Stroyandee76ef2016-01-07 15:35:37 -07002215 errorcodeentry = ''
2216 errorcodes = errorcodes.split(',')
Dustin Graves3ff520c2016-03-28 16:17:38 -06002217 return '* ename:' + '\n* ename:'.join(errorcodes)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002218
2219 return None
2220
2221 #
2222 # Command generation
2223 def genCmd(self, cmdinfo, name):
2224 OutputGenerator.genCmd(self, cmdinfo, name)
2225 #
Dustin Graves3ff520c2016-03-28 16:17:38 -06002226 # Get all the parameters
Mike Stroyandee76ef2016-01-07 15:35:37 -07002227 params = cmdinfo.elem.findall('param')
Dustin Graves3ff520c2016-03-28 16:17:38 -06002228 usageelements = cmdinfo.elem.findall('validity/usage')
2229 usages = []
2230
2231 for usage in usageelements:
2232 usages.append(usage.text)
2233 for usage in cmdinfo.additionalValidity:
2234 usages.append(usage.text)
2235 for usage in cmdinfo.removedValidity:
2236 usages.remove(usage.text)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002237
2238 validity = self.makeValidUsageStatements(cmdinfo.elem, name, params, usages)
2239 threadsafety = self.makeThreadSafetyBlock(cmdinfo.elem, 'param')
2240 commandpropertiesentry = self.makeCommandPropertiesTableEntry(cmdinfo.elem, name)
2241 successcodes = self.makeSuccessCodes(cmdinfo.elem, name)
2242 errorcodes = self.makeErrorCodes(cmdinfo.elem, name)
2243
2244 self.writeInclude('validity/protos', name, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes)
2245
2246 #
2247 # Struct Generation
2248 def genStruct(self, typeinfo, typename):
2249 OutputGenerator.genStruct(self, typeinfo, typename)
2250
2251 # Anything that's only ever returned can't be set by the user, so shouldn't have any validity information.
2252 if typeinfo.elem.attrib.get('returnedonly') is None:
2253 params = typeinfo.elem.findall('member')
Dustin Graves3ff520c2016-03-28 16:17:38 -06002254
2255 usageelements = typeinfo.elem.findall('validity/usage')
2256 usages = []
2257
2258 for usage in usageelements:
2259 usages.append(usage.text)
2260 for usage in typeinfo.additionalValidity:
2261 usages.append(usage.text)
2262 for usage in typeinfo.removedValidity:
2263 usages.remove(usage.text)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002264
2265 validity = self.makeValidUsageStatements(typeinfo.elem, typename, params, usages)
2266 threadsafety = self.makeThreadSafetyBlock(typeinfo.elem, 'member')
2267
2268 self.writeInclude('validity/structs', typename, validity, threadsafety, None, None, None)
2269 else:
2270 # Still generate files for return only structs, in case this state changes later
2271 self.writeInclude('validity/structs', typename, None, None, None, None, None)
2272
2273 #
2274 # Type Generation
2275 def genType(self, typeinfo, typename):
2276 OutputGenerator.genType(self, typeinfo, typename)
2277
2278 category = typeinfo.elem.get('category')
2279 if (category == 'struct' or category == 'union'):
2280 self.genStruct(typeinfo, typename)
2281
2282# HostSynchronizationOutputGenerator - subclass of OutputGenerator.
2283# Generates AsciiDoc includes of the externsync parameter table for the
2284# fundamentals chapter of the Vulkan specification. Similar to
2285# DocOutputGenerator.
2286#
2287# ---- methods ----
2288# HostSynchronizationOutputGenerator(errFile, warnFile, diagFile) - args as for
2289# OutputGenerator. Defines additional internal state.
2290# ---- methods overriding base class ----
2291# genCmd(cmdinfo)
2292class HostSynchronizationOutputGenerator(OutputGenerator):
2293 # Generate Host Synchronized Parameters in a table at the top of the spec
2294 def __init__(self,
2295 errFile = sys.stderr,
2296 warnFile = sys.stderr,
2297 diagFile = sys.stdout):
2298 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2299
2300 threadsafety = {'parameters': '', 'parameterlists': '', 'implicit': ''}
2301
2302 def makeParameterName(self, name):
2303 return 'pname:' + name
2304
2305 def makeFLink(self, name):
2306 return 'flink:' + name
2307
2308 #
2309 # Generate an include file
2310 #
2311 # directory - subdirectory to put file in
2312 # basename - base name of the file
2313 # contents - contents of the file (Asciidoc boilerplate aside)
2314 def writeInclude(self):
2315
2316 if self.threadsafety['parameters'] is not None:
2317 # Create file
2318 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameters.txt'
2319 self.logMsg('diag', '# Generating include file:', filename)
2320 fp = open(filename, 'w')
2321
2322 # Host Synchronization
Dustin Graves3ff520c2016-03-28 16:17:38 -06002323 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002324 write('.Externally Synchronized Parameters', file=fp)
2325 write('*' * 80, file=fp)
2326 write(self.threadsafety['parameters'], file=fp, end='')
2327 write('*' * 80, file=fp)
2328 write('', file=fp)
2329
2330 if self.threadsafety['parameterlists'] is not None:
2331 # Create file
2332 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameterlists.txt'
2333 self.logMsg('diag', '# Generating include file:', filename)
2334 fp = open(filename, 'w')
2335
2336 # Host Synchronization
Dustin Graves3ff520c2016-03-28 16:17:38 -06002337 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002338 write('.Externally Synchronized Parameter Lists', file=fp)
2339 write('*' * 80, file=fp)
2340 write(self.threadsafety['parameterlists'], file=fp, end='')
2341 write('*' * 80, file=fp)
2342 write('', file=fp)
2343
2344 if self.threadsafety['implicit'] is not None:
2345 # Create file
2346 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/implicit.txt'
2347 self.logMsg('diag', '# Generating include file:', filename)
2348 fp = open(filename, 'w')
2349
2350 # Host Synchronization
Dustin Graves3ff520c2016-03-28 16:17:38 -06002351 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002352 write('.Implicit Externally Synchronized Parameters', file=fp)
2353 write('*' * 80, file=fp)
2354 write(self.threadsafety['implicit'], file=fp, end='')
2355 write('*' * 80, file=fp)
2356 write('', file=fp)
2357
2358 fp.close()
2359
2360 #
2361 # Check if the parameter passed in is a pointer to an array
2362 def paramIsArray(self, param):
2363 return param.attrib.get('len') is not None
2364
2365 # Check if the parameter passed in is a pointer
2366 def paramIsPointer(self, param):
2367 ispointer = False
2368 paramtype = param.find('type')
2369 if paramtype.tail is not None and '*' in paramtype.tail:
2370 ispointer = True
2371
2372 return ispointer
2373
2374 # Turn the "name[].member[]" notation into plain English.
2375 def makeThreadDereferenceHumanReadable(self, dereference):
2376 matches = re.findall(r"[\w]+[^\w]*",dereference)
2377 stringval = ''
2378 for match in reversed(matches):
2379 if '->' in match or '.' in match:
2380 stringval += 'member of '
2381 if '[]' in match:
2382 stringval += 'each element of '
2383
2384 stringval += 'the '
2385 stringval += self.makeParameterName(re.findall(r"[\w]+",match)[0])
2386 stringval += ' '
2387
2388 stringval += 'parameter'
2389
2390 return stringval[0].upper() + stringval[1:]
2391
2392 def makeThreadSafetyBlocks(self, cmd, paramtext):
2393 protoname = cmd.find('proto/name').text
2394
2395 # Find and add any parameters that are thread unsafe
2396 explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
2397 if (explicitexternsyncparams is not None):
2398 for param in explicitexternsyncparams:
2399 externsyncattribs = param.attrib.get('externsync')
2400 paramname = param.find('name')
2401 for externsyncattrib in externsyncattribs.split(','):
2402
2403 tempstring = '* '
2404 if externsyncattrib == 'true':
2405 if self.paramIsArray(param):
2406 tempstring += 'Each element of the '
2407 elif self.paramIsPointer(param):
2408 tempstring += 'The object referenced by the '
2409 else:
2410 tempstring += 'The '
2411
2412 tempstring += self.makeParameterName(paramname.text)
2413 tempstring += ' parameter'
2414
2415 else:
2416 tempstring += self.makeThreadDereferenceHumanReadable(externsyncattrib)
2417
2418 tempstring += ' in '
2419 tempstring += self.makeFLink(protoname)
2420 tempstring += '\n'
2421
2422
2423 if ' element of ' in tempstring:
2424 self.threadsafety['parameterlists'] += tempstring
2425 else:
2426 self.threadsafety['parameters'] += tempstring
2427
2428
2429 # Find and add any "implicit" parameters that are thread unsafe
2430 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2431 if (implicitexternsyncparams is not None):
2432 for elem in implicitexternsyncparams:
2433 self.threadsafety['implicit'] += '* '
2434 self.threadsafety['implicit'] += elem.text[0].upper()
2435 self.threadsafety['implicit'] += elem.text[1:]
2436 self.threadsafety['implicit'] += ' in '
2437 self.threadsafety['implicit'] += self.makeFLink(protoname)
2438 self.threadsafety['implicit'] += '\n'
2439
2440
2441 # For any vkCmd* functions, the commandBuffer parameter must be being recorded
2442 if protoname is not None and 'vkCmd' in protoname:
2443 self.threadsafety['implicit'] += '* '
2444 self.threadsafety['implicit'] += 'The sname:VkCommandPool that pname:commandBuffer was allocated from, in '
2445 self.threadsafety['implicit'] += self.makeFLink(protoname)
2446
2447 self.threadsafety['implicit'] += '\n'
2448
2449 #
2450 # Command generation
2451 def genCmd(self, cmdinfo, name):
2452 OutputGenerator.genCmd(self, cmdinfo, name)
2453 #
2454 # Get all thh parameters
2455 params = cmdinfo.elem.findall('param')
2456 usages = cmdinfo.elem.findall('validity/usage')
2457
2458 self.makeThreadSafetyBlocks(cmdinfo.elem, 'param')
2459
2460 self.writeInclude()
Mike Stroyan845bdc42015-11-02 15:30:20 -07002461
2462# ThreadOutputGenerator - subclass of OutputGenerator.
2463# Generates Thread checking framework
2464#
2465# ---- methods ----
2466# ThreadOutputGenerator(errFile, warnFile, diagFile) - args as for
2467# OutputGenerator. Defines additional internal state.
2468# ---- methods overriding base class ----
2469# beginFile(genOpts)
2470# endFile()
2471# beginFeature(interface, emit)
2472# endFeature()
2473# genType(typeinfo,name)
2474# genStruct(typeinfo,name)
2475# genGroup(groupinfo,name)
2476# genEnum(enuminfo, name)
2477# genCmd(cmdinfo)
2478class ThreadOutputGenerator(OutputGenerator):
2479 """Generate specified API interfaces in a specific style, such as a C header"""
2480 # This is an ordered list of sections in the header file.
2481 TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
2482 'group', 'bitmask', 'funcpointer', 'struct']
2483 ALL_SECTIONS = TYPE_SECTIONS + ['command']
2484 def __init__(self,
2485 errFile = sys.stderr,
2486 warnFile = sys.stderr,
2487 diagFile = sys.stdout):
2488 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2489 # Internal state - accumulators for different inner block text
2490 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2491 self.intercepts = []
2492
2493 # Check if the parameter passed in is a pointer to an array
2494 def paramIsArray(self, param):
2495 return param.attrib.get('len') is not None
2496
2497 # Check if the parameter passed in is a pointer
2498 def paramIsPointer(self, param):
2499 ispointer = False
2500 for elem in param:
2501 #write('paramIsPointer '+elem.text, file=sys.stderr)
2502 #write('elem.tag '+elem.tag, file=sys.stderr)
2503 #if (elem.tail is None):
2504 # write('elem.tail is None', file=sys.stderr)
2505 #else:
2506 # write('elem.tail '+elem.tail, file=sys.stderr)
2507 if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
2508 ispointer = True
2509 # write('is pointer', file=sys.stderr)
2510 return ispointer
2511 def makeThreadUseBlock(self, cmd, functionprefix):
2512 """Generate C function pointer typedef for <command> Element"""
2513 paramdecl = ''
2514 thread_check_dispatchable_objects = [
2515 "VkCommandBuffer",
2516 "VkDevice",
2517 "VkInstance",
2518 "VkQueue",
2519 ]
2520 thread_check_nondispatchable_objects = [
2521 "VkBuffer",
2522 "VkBufferView",
2523 "VkCommandPool",
2524 "VkDescriptorPool",
2525 "VkDescriptorSetLayout",
2526 "VkDeviceMemory",
2527 "VkEvent",
2528 "VkFence",
2529 "VkFramebuffer",
2530 "VkImage",
2531 "VkImageView",
2532 "VkPipeline",
2533 "VkPipelineCache",
2534 "VkPipelineLayout",
2535 "VkQueryPool",
2536 "VkRenderPass",
2537 "VkSampler",
2538 "VkSemaphore",
2539 "VkShaderModule",
2540 ]
2541
2542 # Find and add any parameters that are thread unsafe
2543 params = cmd.findall('param')
2544 for param in params:
2545 paramname = param.find('name')
2546 if False: # self.paramIsPointer(param):
2547 paramdecl += ' // not watching use of pointer ' + paramname.text + '\n'
2548 else:
2549 externsync = param.attrib.get('externsync')
2550 if externsync == 'true':
2551 if self.paramIsArray(param):
Michael Mc Donnellc133bd02016-03-17 21:18:32 -07002552 paramdecl += ' for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
Mike Stroyan845bdc42015-11-02 15:30:20 -07002553 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + '[index]);\n'
2554 paramdecl += ' }\n'
2555 else:
2556 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + ');\n'
2557 elif (param.attrib.get('externsync')):
2558 if self.paramIsArray(param):
2559 # Externsync can list pointers to arrays of members to synchronize
Michael Mc Donnellc133bd02016-03-17 21:18:32 -07002560 paramdecl += ' for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
Mike Stroyan845bdc42015-11-02 15:30:20 -07002561 for member in externsync.split(","):
2562 # Replace first empty [] in member name with index
2563 element = member.replace('[]','[index]',1)
2564 if '[]' in element:
2565 # Replace any second empty [] in element name with
2566 # inner array index based on mapping array names like
2567 # "pSomeThings[]" to "someThingCount" array size.
2568 # This could be more robust by mapping a param member
2569 # name to a struct type and "len" attribute.
2570 limit = element[0:element.find('s[]')] + 'Count'
2571 dotp = limit.rfind('.p')
2572 limit = limit[0:dotp+1] + limit[dotp+2:dotp+3].lower() + limit[dotp+3:]
Michael Mc Donnellc133bd02016-03-17 21:18:32 -07002573 paramdecl += ' for(uint32_t index2=0;index2<'+limit+';index2++)'
Mike Stroyan845bdc42015-11-02 15:30:20 -07002574 element = element.replace('[]','[index2]')
2575 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + element + ');\n'
2576 paramdecl += ' }\n'
2577 else:
2578 # externsync can list members to synchronize
2579 for member in externsync.split(","):
2580 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + member + ');\n'
2581 else:
2582 paramtype = param.find('type')
2583 if paramtype is not None:
2584 paramtype = paramtype.text
2585 else:
2586 paramtype = 'None'
2587 if paramtype in thread_check_dispatchable_objects or paramtype in thread_check_nondispatchable_objects:
2588 if self.paramIsArray(param) and ('pPipelines' != paramname.text):
Michael Mc Donnellc133bd02016-03-17 21:18:32 -07002589 paramdecl += ' for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
Mike Stroyan845bdc42015-11-02 15:30:20 -07002590 paramdecl += ' ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + '[index]);\n'
2591 paramdecl += ' }\n'
2592 elif not self.paramIsPointer(param):
2593 # Pointer params are often being created.
2594 # They are not being read from.
2595 paramdecl += ' ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + ');\n'
2596 explicitexternsyncparams = cmd.findall("param[@externsync]")
2597 if (explicitexternsyncparams is not None):
2598 for param in explicitexternsyncparams:
2599 externsyncattrib = param.attrib.get('externsync')
2600 paramname = param.find('name')
2601 paramdecl += '// Host access to '
2602 if externsyncattrib == 'true':
2603 if self.paramIsArray(param):
2604 paramdecl += 'each member of ' + paramname.text
2605 elif self.paramIsPointer(param):
2606 paramdecl += 'the object referenced by ' + paramname.text
2607 else:
2608 paramdecl += paramname.text
2609 else:
2610 paramdecl += externsyncattrib
2611 paramdecl += ' must be externally synchronized\n'
2612
2613 # Find and add any "implicit" parameters that are thread unsafe
2614 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2615 if (implicitexternsyncparams is not None):
2616 for elem in implicitexternsyncparams:
2617 paramdecl += ' // '
2618 paramdecl += elem.text
2619 paramdecl += ' must be externally synchronized between host accesses\n'
2620
2621 if (paramdecl == ''):
2622 return None
2623 else:
2624 return paramdecl
2625 def beginFile(self, genOpts):
2626 OutputGenerator.beginFile(self, genOpts)
2627 # C-specific
2628 #
2629 # Multiple inclusion protection & C++ wrappers.
2630 if (genOpts.protectFile and self.genOpts.filename):
2631 headerSym = '__' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename))
2632 write('#ifndef', headerSym, file=self.outFile)
2633 write('#define', headerSym, '1', file=self.outFile)
2634 self.newline()
2635 write('#ifdef __cplusplus', file=self.outFile)
2636 write('extern "C" {', file=self.outFile)
2637 write('#endif', file=self.outFile)
2638 self.newline()
2639 #
2640 # User-supplied prefix text, if any (list of strings)
2641 if (genOpts.prefixText):
2642 for s in genOpts.prefixText:
2643 write(s, file=self.outFile)
2644 def endFile(self):
2645 # C-specific
2646 # Finish C++ wrapper and multiple inclusion protection
2647 self.newline()
2648 # record intercepted procedures
2649 write('// intercepts', file=self.outFile)
2650 write('struct { const char* name; PFN_vkVoidFunction pFunc;} procmap[] = {', file=self.outFile)
2651 write('\n'.join(self.intercepts), file=self.outFile)
2652 write('};\n', file=self.outFile)
2653 self.newline()
2654 write('#ifdef __cplusplus', file=self.outFile)
2655 write('}', file=self.outFile)
2656 write('#endif', file=self.outFile)
2657 if (self.genOpts.protectFile and self.genOpts.filename):
2658 self.newline()
2659 write('#endif', file=self.outFile)
2660 # Finish processing in superclass
2661 OutputGenerator.endFile(self)
2662 def beginFeature(self, interface, emit):
2663 #write('// starting beginFeature', file=self.outFile)
2664 # Start processing in superclass
2665 OutputGenerator.beginFeature(self, interface, emit)
2666 # C-specific
2667 # Accumulate includes, defines, types, enums, function pointer typedefs,
2668 # end function prototypes separately for this feature. They're only
2669 # printed in endFeature().
2670 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2671 #write('// ending beginFeature', file=self.outFile)
2672 def endFeature(self):
2673 # C-specific
2674 # Actually write the interface to the output file.
2675 #write('// starting endFeature', file=self.outFile)
2676 if (self.emit):
2677 self.newline()
2678 if (self.genOpts.protectFeature):
2679 write('#ifndef', self.featureName, file=self.outFile)
2680 # If type declarations are needed by other features based on
2681 # this one, it may be necessary to suppress the ExtraProtect,
2682 # or move it below the 'for section...' loop.
2683 #write('// endFeature looking at self.featureExtraProtect', file=self.outFile)
2684 if (self.featureExtraProtect != None):
2685 write('#ifdef', self.featureExtraProtect, file=self.outFile)
2686 #write('#define', self.featureName, '1', file=self.outFile)
2687 for section in self.TYPE_SECTIONS:
2688 #write('// endFeature writing section'+section, file=self.outFile)
2689 contents = self.sections[section]
2690 if contents:
2691 write('\n'.join(contents), file=self.outFile)
2692 self.newline()
2693 #write('// endFeature looking at self.sections[command]', file=self.outFile)
2694 if (self.sections['command']):
2695 write('\n'.join(self.sections['command']), end='', file=self.outFile)
2696 self.newline()
2697 if (self.featureExtraProtect != None):
2698 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
2699 if (self.genOpts.protectFeature):
2700 write('#endif /*', self.featureName, '*/', file=self.outFile)
2701 # Finish processing in superclass
2702 OutputGenerator.endFeature(self)
2703 #write('// ending endFeature', file=self.outFile)
2704 #
2705 # Append a definition to the specified section
2706 def appendSection(self, section, text):
2707 # self.sections[section].append('SECTION: ' + section + '\n')
2708 self.sections[section].append(text)
2709 #
2710 # Type generation
2711 def genType(self, typeinfo, name):
2712 pass
2713 #
2714 # Struct (e.g. C "struct" type) generation.
2715 # This is a special case of the <type> tag where the contents are
2716 # interpreted as a set of <member> tags instead of freeform C
2717 # C type declarations. The <member> tags are just like <param>
2718 # tags - they are a declaration of a struct or union member.
2719 # Only simple member declarations are supported (no nested
2720 # structs etc.)
2721 def genStruct(self, typeinfo, typeName):
2722 OutputGenerator.genStruct(self, typeinfo, typeName)
2723 body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
2724 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
2725 for member in typeinfo.elem.findall('.//member'):
2726 body += self.makeCParamDecl(member, self.genOpts.alignFuncParam)
2727 body += ';\n'
2728 body += '} ' + typeName + ';\n'
2729 self.appendSection('struct', body)
2730 #
2731 # Group (e.g. C "enum" type) generation.
2732 # These are concatenated together with other types.
2733 def genGroup(self, groupinfo, groupName):
2734 pass
2735 # Enumerant generation
2736 # <enum> tags may specify their values in several ways, but are usually
2737 # just integers.
2738 def genEnum(self, enuminfo, name):
2739 pass
2740 #
2741 # Command generation
2742 def genCmd(self, cmdinfo, name):
2743 special_functions = [
2744 'vkGetDeviceProcAddr',
2745 'vkGetInstanceProcAddr',
2746 'vkCreateDevice',
2747 'vkDestroyDevice',
2748 'vkCreateInstance',
2749 'vkDestroyInstance',
2750 'vkEnumerateInstanceLayerProperties',
2751 'vkEnumerateInstanceExtensionProperties',
2752 'vkAllocateCommandBuffers',
2753 'vkFreeCommandBuffers',
2754 'vkCreateDebugReportCallbackEXT',
2755 'vkDestroyDebugReportCallbackEXT',
2756 ]
2757 if name in special_functions:
Michael Lentine0a369f62016-02-03 16:51:46 -06002758 self.intercepts += [ ' {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name) ]
Mike Stroyan845bdc42015-11-02 15:30:20 -07002759 return
2760 if "KHR" in name:
2761 self.appendSection('command', '// TODO - not wrapping KHR function ' + name)
2762 return
2763 # Determine first if this function needs to be intercepted
2764 startthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'start')
2765 if startthreadsafety is None:
2766 return
2767 finishthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'finish')
2768 # record that the function will be intercepted
2769 if (self.featureExtraProtect != None):
2770 self.intercepts += [ '#ifdef %s' % self.featureExtraProtect ]
Michael Lentine0a369f62016-02-03 16:51:46 -06002771 self.intercepts += [ ' {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name) ]
Mike Stroyan845bdc42015-11-02 15:30:20 -07002772 if (self.featureExtraProtect != None):
2773 self.intercepts += [ '#endif' ]
2774
2775 OutputGenerator.genCmd(self, cmdinfo, name)
2776 #
2777 decls = self.makeCDecls(cmdinfo.elem)
2778 self.appendSection('command', '')
2779 self.appendSection('command', decls[0][:-1])
2780 self.appendSection('command', '{')
2781 # setup common to call wrappers
2782 # first parameter is always dispatchable
2783 dispatchable_type = cmdinfo.elem.find('param/type').text
2784 dispatchable_name = cmdinfo.elem.find('param/name').text
2785 self.appendSection('command', ' dispatch_key key = get_dispatch_key('+dispatchable_name+');')
2786 self.appendSection('command', ' layer_data *my_data = get_my_data_ptr(key, layer_data_map);')
2787 if dispatchable_type in ["VkPhysicalDevice", "VkInstance"]:
2788 self.appendSection('command', ' VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table;')
2789 else:
2790 self.appendSection('command', ' VkLayerDispatchTable *pTable = my_data->device_dispatch_table;')
2791 # Declare result variable, if any.
2792 resulttype = cmdinfo.elem.find('proto/type')
2793 if (resulttype != None and resulttype.text == 'void'):
2794 resulttype = None
2795 if (resulttype != None):
2796 self.appendSection('command', ' ' + resulttype.text + ' result;')
2797 assignresult = 'result = '
2798 else:
2799 assignresult = ''
2800
2801 self.appendSection('command', str(startthreadsafety))
2802 params = cmdinfo.elem.findall('param/name')
2803 paramstext = ','.join([str(param.text) for param in params])
2804 API = cmdinfo.elem.attrib.get('name').replace('vk','pTable->',1)
2805 self.appendSection('command', ' ' + assignresult + API + '(' + paramstext + ');')
2806 self.appendSection('command', str(finishthreadsafety))
2807 # Return result variable, if any.
2808 if (resulttype != None):
2809 self.appendSection('command', ' return result;')
2810 self.appendSection('command', '}')
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002811
2812# ParamCheckerOutputGenerator - subclass of OutputGenerator.
2813# Generates param checker layer code.
2814#
2815# ---- methods ----
2816# ParamCheckerOutputGenerator(errFile, warnFile, diagFile) - args as for
2817# OutputGenerator. Defines additional internal state.
2818# ---- methods overriding base class ----
2819# beginFile(genOpts)
2820# endFile()
2821# beginFeature(interface, emit)
2822# endFeature()
2823# genType(typeinfo,name)
2824# genStruct(typeinfo,name)
2825# genGroup(groupinfo,name)
2826# genEnum(enuminfo, name)
2827# genCmd(cmdinfo)
2828class ParamCheckerOutputGenerator(OutputGenerator):
2829 """Generate ParamChecker code based on XML element attributes"""
2830 # This is an ordered list of sections in the header file.
2831 ALL_SECTIONS = ['command']
2832 def __init__(self,
2833 errFile = sys.stderr,
2834 warnFile = sys.stderr,
2835 diagFile = sys.stdout):
2836 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2837 self.INDENT_SPACES = 4
2838 # Commands to ignore
Dustin Graveseec48bf2016-03-02 18:23:29 -07002839 self.blacklist = [
Dustin Graves842621d2016-03-03 14:17:08 -07002840 'vkGetInstanceProcAddr',
2841 'vkGetDeviceProcAddr',
Dustin Graveseec48bf2016-03-02 18:23:29 -07002842 'vkEnumerateInstanceLayerProperties',
2843 'vkEnumerateInstanceExtensionsProperties',
2844 'vkEnumerateDeviceLayerProperties',
2845 'vkEnumerateDeviceExtensionsProperties',
2846 'vkCreateDebugReportCallbackEXT',
2847 'vkDebugReportMessageEXT']
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06002848 # Validation conditions for some special case struct members that are conditionally validated
2849 self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002850 # Internal state - accumulators for different inner block text
2851 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
Dustin Graveseec48bf2016-03-02 18:23:29 -07002852 self.structNames = [] # List of Vulkan struct typenames
2853 self.stypes = [] # Values from the VkStructureType enumeration
2854 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
Dustin Graves20fd66f2016-04-18 18:33:21 -06002855 self.handleTypes = set() # Set of handle type names
Dustin Graveseec48bf2016-03-02 18:23:29 -07002856 self.commands = [] # List of CommandData records for all Vulkan commands
2857 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06002858 self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type
Dustin Graves29148ff2016-03-23 19:44:00 -06002859 self.enumRanges = dict() # Map of enum name to BEGIN/END range values
Dustin Graves9c6b62b2016-04-26 15:37:10 -06002860 self.flags = dict() # Map of flags typenames to a Boolean value indicating that validation code is generated for a value of this type
2861 self.flagBits = dict() # Map of flag bits typename to list of values
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002862 # Named tuples to store struct and command data
2863 self.StructType = namedtuple('StructType', ['name', 'value'])
Dustin Graves29148ff2016-03-23 19:44:00 -06002864 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
Dustin Graves20fd66f2016-04-18 18:33:21 -06002865 'isconst', 'isoptional', 'iscount', 'noautovalidity', 'len', 'extstructs',
2866 'condition', 'cdecl'])
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002867 self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl'])
Dustin Graveseec48bf2016-03-02 18:23:29 -07002868 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002869 #
2870 def incIndent(self, indent):
2871 inc = ' ' * self.INDENT_SPACES
2872 if indent:
2873 return indent + inc
2874 return inc
Dustin Graves842621d2016-03-03 14:17:08 -07002875 #
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002876 def decIndent(self, indent):
2877 if indent and (len(indent) > self.INDENT_SPACES):
2878 return indent[:-self.INDENT_SPACES]
2879 return ''
2880 #
2881 def beginFile(self, genOpts):
2882 OutputGenerator.beginFile(self, genOpts)
2883 # C-specific
2884 #
2885 # User-supplied prefix text, if any (list of strings)
2886 if (genOpts.prefixText):
2887 for s in genOpts.prefixText:
2888 write(s, file=self.outFile)
2889 #
2890 # Multiple inclusion protection & C++ wrappers.
2891 if (genOpts.protectFile and self.genOpts.filename):
2892 headerSym = re.sub('\.h', '_H', os.path.basename(self.genOpts.filename)).upper()
2893 write('#ifndef', headerSym, file=self.outFile)
2894 write('#define', headerSym, '1', file=self.outFile)
2895 self.newline()
2896 #
2897 # Headers
Dustin Gravesb210cee2016-03-31 09:50:42 -06002898 write('#include <string>', file=self.outFile)
2899 self.newline()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002900 write('#include "vulkan/vulkan.h"', file=self.outFile)
Dustin Graves58c2f662016-03-08 17:48:20 -07002901 write('#include "vk_layer_extension_utils.h"', file=self.outFile)
Mark Lobodzinski739391a2016-03-17 15:08:18 -06002902 write('#include "parameter_validation_utils.h"', file=self.outFile)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002903 #
2904 # Macros
2905 self.newline()
2906 write('#ifndef UNUSED_PARAMETER', file=self.outFile)
2907 write('#define UNUSED_PARAMETER(x) (void)(x)', file=self.outFile)
2908 write('#endif // UNUSED_PARAMETER', file=self.outFile)
Dustin Gravesb83fc2d2016-05-04 12:56:08 -06002909 #
2910 # Namespace
2911 self.newline()
2912 write('namespace parameter_validation {', file = self.outFile)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002913 def endFile(self):
2914 # C-specific
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002915 self.newline()
Dustin Gravesb83fc2d2016-05-04 12:56:08 -06002916 # Namespace
2917 write('} // namespace parameter_validation', file = self.outFile)
2918 # Finish C++ wrapper and multiple inclusion protection
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002919 if (self.genOpts.protectFile and self.genOpts.filename):
2920 self.newline()
2921 write('#endif', file=self.outFile)
2922 # Finish processing in superclass
2923 OutputGenerator.endFile(self)
2924 def beginFeature(self, interface, emit):
2925 # Start processing in superclass
2926 OutputGenerator.beginFeature(self, interface, emit)
2927 # C-specific
2928 # Accumulate includes, defines, types, enums, function pointer typedefs,
2929 # end function prototypes separately for this feature. They're only
2930 # printed in endFeature().
2931 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
Dustin Graveseec48bf2016-03-02 18:23:29 -07002932 self.structNames = []
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002933 self.stypes = []
2934 self.structTypes = dict()
Dustin Graves20fd66f2016-04-18 18:33:21 -06002935 self.handleTypes = set()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002936 self.commands = []
Dustin Graveseec48bf2016-03-02 18:23:29 -07002937 self.structMembers = []
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06002938 self.validatedStructs = dict()
Dustin Graves29148ff2016-03-23 19:44:00 -06002939 self.enumRanges = dict()
Dustin Graves9c6b62b2016-04-26 15:37:10 -06002940 self.flags = dict()
2941 self.flagBits = dict()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002942 def endFeature(self):
2943 # C-specific
2944 # Actually write the interface to the output file.
2945 if (self.emit):
2946 self.newline()
2947 # If type declarations are needed by other features based on
2948 # this one, it may be necessary to suppress the ExtraProtect,
2949 # or move it below the 'for section...' loop.
2950 if (self.featureExtraProtect != None):
2951 write('#ifdef', self.featureExtraProtect, file=self.outFile)
Dustin Graveseec48bf2016-03-02 18:23:29 -07002952 # Generate the struct member checking code from the captured data
Dustin Graveseec48bf2016-03-02 18:23:29 -07002953 self.processStructMemberData()
2954 # Generate the command parameter checking code from the captured data
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002955 self.processCmdData()
Dustin Graves9c6b62b2016-04-26 15:37:10 -06002956 # Write the declarations for the VkFlags values combining all flag bits
2957 for flag in sorted(self.flags):
2958 if self.flags[flag]:
2959 flagBits = flag.replace('Flags', 'FlagBits')
2960 bits = self.flagBits[flagBits]
2961 decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
2962 for bit in bits[1:]:
2963 decl += '|' + bit
2964 decl += ';'
2965 write(decl, file=self.outFile)
2966 self.newline()
2967 # Write the parameter validation code to the file
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002968 if (self.sections['command']):
2969 if (self.genOpts.protectProto):
2970 write(self.genOpts.protectProto,
2971 self.genOpts.protectProtoStr, file=self.outFile)
2972 write('\n'.join(self.sections['command']), end='', file=self.outFile)
2973 if (self.featureExtraProtect != None):
2974 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
2975 else:
2976 self.newline()
2977 # Finish processing in superclass
2978 OutputGenerator.endFeature(self)
2979 #
2980 # Append a definition to the specified section
2981 def appendSection(self, section, text):
2982 # self.sections[section].append('SECTION: ' + section + '\n')
2983 self.sections[section].append(text)
2984 #
2985 # Type generation
2986 def genType(self, typeinfo, name):
2987 OutputGenerator.genType(self, typeinfo, name)
2988 typeElem = typeinfo.elem
2989 # If the type is a struct type, traverse the imbedded <member> tags
2990 # generating a structure. Otherwise, emit the tag text.
2991 category = typeElem.get('category')
2992 if (category == 'struct' or category == 'union'):
Dustin Graveseec48bf2016-03-02 18:23:29 -07002993 self.structNames.append(name)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002994 self.genStruct(typeinfo, name)
Dustin Graves20fd66f2016-04-18 18:33:21 -06002995 elif (category == 'handle'):
2996 self.handleTypes.add(name)
Dustin Graves9c6b62b2016-04-26 15:37:10 -06002997 elif (category == 'bitmask'):
2998 self.flags[name] = False
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002999 #
3000 # Struct parameter check generation.
3001 # This is a special case of the <type> tag where the contents are
3002 # interpreted as a set of <member> tags instead of freeform C
3003 # C type declarations. The <member> tags are just like <param>
3004 # tags - they are a declaration of a struct or union member.
3005 # Only simple member declarations are supported (no nested
3006 # structs etc.)
3007 def genStruct(self, typeinfo, typeName):
3008 OutputGenerator.genStruct(self, typeinfo, typeName)
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003009 conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
Dustin Graveseec48bf2016-03-02 18:23:29 -07003010 members = typeinfo.elem.findall('.//member')
3011 #
3012 # Iterate over members once to get length parameters for arrays
3013 lens = set()
3014 for member in members:
3015 len = self.getLen(member)
3016 if len:
3017 lens.add(len)
3018 #
3019 # Generate member info
3020 membersInfo = []
3021 for member in members:
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003022 # Get the member's type and name
Dustin Graveseec48bf2016-03-02 18:23:29 -07003023 info = self.getTypeNameTuple(member)
3024 type = info[0]
3025 name = info[1]
3026 stypeValue = ''
Dustin Graves29148ff2016-03-23 19:44:00 -06003027 cdecl = self.makeCParamDecl(member, 0)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003028 # Process VkStructureType
3029 if type == 'VkStructureType':
3030 # Extract the required struct type value from the comments
3031 # embedded in the original text defining the 'typeinfo' element
3032 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
Dustin Graveseec48bf2016-03-02 18:23:29 -07003033 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003034 if result:
3035 value = result.group(0)
3036 # Make sure value is valid
3037 #if value not in self.stypes:
3038 # print('WARNING: {} is not part of the VkStructureType enumeration [{}]'.format(value, typeName))
3039 else:
Mike Stroyan3c5a6e22016-04-05 16:40:30 -06003040 value = typeName
3041 # Remove EXT
3042 value = re.sub('EXT', '', value)
3043 # Add underscore between lowercase then uppercase
3044 value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', value)
3045 # Change to uppercase
3046 value = value.upper()
3047 # Add STRUCTURE_TYPE_
3048 value = re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003049 # Store the required type value
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003050 self.structTypes[typeName] = self.StructType(name=name, value=value)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003051 #
3052 # Store pointer/array/string info
3053 # Check for parameter name in lens set
3054 iscount = False
3055 if name in lens:
3056 iscount = True
3057 # The pNext members are not tagged as optional, but are treated as
3058 # optional for parameter NULL checks. Static array members
3059 # are also treated as optional to skip NULL pointer validation, as
3060 # they won't be NULL.
3061 isstaticarray = self.paramIsStaticArray(member)
3062 isoptional = False
3063 if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
3064 isoptional = True
3065 membersInfo.append(self.CommandParam(type=type, name=name,
3066 ispointer=self.paramIsPointer(member),
3067 isstaticarray=isstaticarray,
Dustin Graves29148ff2016-03-23 19:44:00 -06003068 isbool=True if type == 'VkBool32' else False,
3069 israngedenum=True if type in self.enumRanges else False,
3070 isconst=True if 'const' in cdecl else False,
Dustin Graveseec48bf2016-03-02 18:23:29 -07003071 isoptional=isoptional,
3072 iscount=iscount,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003073 noautovalidity=True if member.attrib.get('noautovalidity') is not None else False,
Dustin Graveseec48bf2016-03-02 18:23:29 -07003074 len=self.getLen(member),
Dustin Graves58c2f662016-03-08 17:48:20 -07003075 extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None,
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003076 condition=conditions[name] if conditions and name in conditions else None,
Dustin Graves29148ff2016-03-23 19:44:00 -06003077 cdecl=cdecl))
Dustin Graveseec48bf2016-03-02 18:23:29 -07003078 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003079 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003080 # Capture group (e.g. C "enum" type) info to be used for
3081 # param check code generation.
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003082 # These are concatenated together with other types.
3083 def genGroup(self, groupinfo, groupName):
3084 OutputGenerator.genGroup(self, groupinfo, groupName)
Dustin Graves29148ff2016-03-23 19:44:00 -06003085 groupElem = groupinfo.elem
3086 #
3087 # Store the sType values
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003088 if groupName == 'VkStructureType':
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003089 for elem in groupElem.findall('enum'):
Dustin Graves29148ff2016-03-23 19:44:00 -06003090 self.stypes.append(elem.get('name'))
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003091 elif 'FlagBits' in groupName:
3092 bits = []
3093 for elem in groupElem.findall('enum'):
3094 bits.append(elem.get('name'))
3095 if bits:
3096 self.flagBits[groupName] = bits
Dustin Graves29148ff2016-03-23 19:44:00 -06003097 else:
3098 # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
3099 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
3100 expandPrefix = expandName
3101 expandSuffix = ''
3102 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
3103 if expandSuffixMatch:
3104 expandSuffix = '_' + expandSuffixMatch.group()
3105 # Strip off the suffix from the prefix
3106 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
3107 isEnum = ('FLAG_BITS' not in expandPrefix)
3108 if isEnum:
3109 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003110 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003111 # Capture command parameter info to be used for param
3112 # check code generation.
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003113 def genCmd(self, cmdinfo, name):
3114 OutputGenerator.genCmd(self, cmdinfo, name)
3115 if name not in self.blacklist:
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003116 params = cmdinfo.elem.findall('param')
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003117 # Get list of array lengths
3118 lens = set()
3119 for param in params:
3120 len = self.getLen(param)
3121 if len:
3122 lens.add(len)
3123 # Get param info
3124 paramsInfo = []
3125 for param in params:
3126 paramInfo = self.getTypeNameTuple(param)
Dustin Graves29148ff2016-03-23 19:44:00 -06003127 cdecl = self.makeCParamDecl(param, 0)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003128 # Check for parameter name in lens set
3129 iscount = False
3130 if paramInfo[1] in lens:
3131 iscount = True
3132 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
3133 ispointer=self.paramIsPointer(param),
3134 isstaticarray=self.paramIsStaticArray(param),
Dustin Graves29148ff2016-03-23 19:44:00 -06003135 isbool=True if paramInfo[0] == 'VkBool32' else False,
3136 israngedenum=True if paramInfo[0] in self.enumRanges else False,
3137 isconst=True if 'const' in cdecl else False,
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003138 isoptional=self.paramIsOptional(param),
3139 iscount=iscount,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003140 noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003141 len=self.getLen(param),
Dustin Graves58c2f662016-03-08 17:48:20 -07003142 extstructs=None,
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003143 condition=None,
Dustin Graves29148ff2016-03-23 19:44:00 -06003144 cdecl=cdecl))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003145 self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0]))
3146 #
3147 # Check if the parameter passed in is a pointer
3148 def paramIsPointer(self, param):
Dustin Graveseec48bf2016-03-02 18:23:29 -07003149 ispointer = 0
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003150 paramtype = param.find('type')
Dustin Graveseec48bf2016-03-02 18:23:29 -07003151 if (paramtype.tail is not None) and ('*' in paramtype.tail):
3152 ispointer = paramtype.tail.count('*')
Dustin Graves27a912a2016-03-07 17:52:14 -07003153 elif paramtype.text[:4] == 'PFN_':
3154 # Treat function pointer typedefs as a pointer to a single value
3155 ispointer = 1
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003156 return ispointer
3157 #
3158 # Check if the parameter passed in is a static array
3159 def paramIsStaticArray(self, param):
Dustin Graveseec48bf2016-03-02 18:23:29 -07003160 isstaticarray = 0
3161 paramname = param.find('name')
3162 if (paramname.tail is not None) and ('[' in paramname.tail):
3163 isstaticarray = paramname.tail.count('[')
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003164 return isstaticarray
3165 #
3166 # Check if the parameter passed in is optional
3167 # Returns a list of Boolean values for comma separated len attributes (len='false,true')
3168 def paramIsOptional(self, param):
3169 # See if the handle is optional
3170 isoptional = False
3171 # Simple, if it's optional, return true
3172 optString = param.attrib.get('optional')
3173 if optString:
3174 if optString == 'true':
3175 isoptional = True
3176 elif ',' in optString:
3177 opts = []
3178 for opt in optString.split(','):
3179 val = opt.strip()
3180 if val == 'true':
3181 opts.append(True)
3182 elif val == 'false':
3183 opts.append(False)
3184 else:
3185 print('Unrecognized len attribute value',val)
3186 isoptional = opts
3187 return isoptional
3188 #
Dustin Graves20fd66f2016-04-18 18:33:21 -06003189 # Check if the handle passed in is optional
3190 # Uses the same logic as ValidityOutputGenerator.isHandleOptional
3191 def isHandleOptional(self, param, lenParam):
3192 # Simple, if it's optional, return true
3193 if param.isoptional:
3194 return True
3195 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
3196 if param.noautovalidity:
3197 return True
3198 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
3199 if lenParam and lenParam.isoptional:
3200 return True
3201 return False
3202 #
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003203 # Retrieve the value of the len tag
3204 def getLen(self, param):
Dustin Graveseec48bf2016-03-02 18:23:29 -07003205 result = None
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003206 len = param.attrib.get('len')
3207 if len and len != 'null-terminated':
Dustin Graveseec48bf2016-03-02 18:23:29 -07003208 # For string arrays, 'len' can look like 'count,null-terminated',
3209 # indicating that we have a null terminated array of strings. We
3210 # strip the null-terminated from the 'len' field and only return
3211 # the parameter specifying the string count
3212 if 'null-terminated' in len:
3213 result = len.split(',')[0]
3214 else:
3215 result = len
3216 return result
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003217 #
3218 # Retrieve the type and name for a parameter
3219 def getTypeNameTuple(self, param):
3220 type = ''
3221 name = ''
3222 for elem in param:
3223 if elem.tag == 'type':
3224 type = noneStr(elem.text)
3225 elif elem.tag == 'name':
3226 name = noneStr(elem.text)
3227 return (type, name)
3228 #
3229 # Find a named parameter in a parameter list
3230 def getParamByName(self, params, name):
3231 for param in params:
3232 if param.name == name:
3233 return param
3234 return None
Dustin Graveseec48bf2016-03-02 18:23:29 -07003235 #
Dustin Graves3ff520c2016-03-28 16:17:38 -06003236 # Extract length values from latexmath. Currently an inflexible solution that looks for specific
3237 # patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
3238 def parseLateXMath(self, source):
3239 name = 'ERROR'
3240 decoratedName = 'ERROR'
3241 if 'mathit' in source:
3242 # Matches expressions similar to 'latexmath:[$\lceil{\mathit{rasterizationSamples} \over 32}\rceil$]'
3243 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)
3244 if not match or match.group(1) != match.group(4):
3245 raise 'Unrecognized latexmath expression'
3246 name = match.group(2)
3247 decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
3248 else:
3249 # Matches expressions similar to 'latexmath : [$dataSize \over 4$]'
3250 match = re.match(r'latexmath\s*\:\s*\[\s*\$\s*(\w+)\s*\\over\s*(\d+)\s*\$\s*\]', source)
3251 name = match.group(1)
3252 decoratedName = '{}/{}'.format(*match.group(1, 2))
3253 return name, decoratedName
3254 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003255 # Get the length paramater record for the specified parameter name
3256 def getLenParam(self, params, name):
3257 lenParam = None
3258 if name:
3259 if '->' in name:
3260 # The count is obtained by dereferencing a member of a struct parameter
Dustin Graves29148ff2016-03-23 19:44:00 -06003261 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003262 isstaticarray=None, isoptional=False, type=None, noautovalidity=False, len=None, extstructs=None,
3263 condition=None, cdecl=None)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003264 elif 'latexmath' in name:
Dustin Graves3ff520c2016-03-28 16:17:38 -06003265 lenName, decoratedName = self.parseLateXMath(name)
3266 lenParam = self.getParamByName(params, lenName)
3267 # TODO: Zero-check the result produced by the equation?
3268 # Copy the stored len parameter entry and overwrite the name with the processed latexmath equation
3269 #param = self.getParamByName(params, lenName)
3270 #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer,
3271 # isoptional=param.isoptional, type=param.type, len=param.len,
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003272 # isstaticarray=param.isstaticarray, extstructs=param.extstructs,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003273 # noautovalidity=True, condition=None, cdecl=param.cdecl)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003274 else:
3275 lenParam = self.getParamByName(params, name)
3276 return lenParam
3277 #
Mark Lobodzinski739391a2016-03-17 15:08:18 -06003278 # Convert a vulkan.h command declaration into a parameter_validation.h definition
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003279 def getCmdDef(self, cmd):
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003280 #
3281 # Strip the trailing ';' and split into individual lines
3282 lines = cmd.cdecl[:-1].split('\n')
3283 # Replace Vulkan prototype
Dustin Graves080069b2016-04-05 13:48:15 -06003284 lines[0] = 'static bool parameter_validation_' + cmd.name + '('
Dustin Graves842621d2016-03-03 14:17:08 -07003285 # Replace the first argument with debug_report_data, when the first
3286 # argument is a handle (not vkCreateInstance)
3287 reportData = ' debug_report_data*'.ljust(self.genOpts.alignFuncParam) + 'report_data,'
3288 if cmd.name != 'vkCreateInstance':
3289 lines[1] = reportData
3290 else:
3291 lines.insert(1, reportData)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003292 return '\n'.join(lines)
3293 #
Dustin Graves35328c12016-02-29 13:35:07 -07003294 # Generate the code to check for a NULL dereference before calling the
3295 # validation function
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003296 def genCheckedLengthCall(self, name, exprs):
Dustin Graves35328c12016-02-29 13:35:07 -07003297 count = name.count('->')
3298 if count:
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003299 checkedExpr = []
3300 localIndent = ''
Dustin Graves35328c12016-02-29 13:35:07 -07003301 elements = name.split('->')
3302 # Open the if expression blocks
3303 for i in range(0, count):
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003304 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
Dustin Graves35328c12016-02-29 13:35:07 -07003305 localIndent = self.incIndent(localIndent)
3306 # Add the validation expression
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003307 for expr in exprs:
3308 checkedExpr.append(localIndent + expr)
Dustin Graves35328c12016-02-29 13:35:07 -07003309 # Close the if blocks
3310 for i in range(0, count):
3311 localIndent = self.decIndent(localIndent)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003312 checkedExpr.append(localIndent + '}\n')
3313 return [checkedExpr]
Dustin Graves35328c12016-02-29 13:35:07 -07003314 # No if statements were required
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003315 return exprs
3316 #
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003317 # Generate code to check for a specific condition before executing validation code
3318 def genConditionalCall(self, prefix, condition, exprs):
3319 checkedExpr = []
3320 localIndent = ''
3321 formattedCondition = condition.format(prefix)
3322 checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
3323 checkedExpr.append(localIndent + '{\n')
3324 localIndent = self.incIndent(localIndent)
3325 for expr in exprs:
3326 checkedExpr.append(localIndent + expr)
3327 localIndent = self.decIndent(localIndent)
3328 checkedExpr.append(localIndent + '}\n')
3329 return [checkedExpr]
3330 #
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003331 # Generate the sType check string
3332 def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName):
3333 checkExpr = []
3334 stype = self.structTypes[value.type]
3335 if lenValue:
3336 # This is an array with a pointer to a count value
3337 if lenValue.ispointer:
3338 # 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 -06003339 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 -06003340 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix))
3341 # This is an array with an integer count value
3342 else:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003343 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 -06003344 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix))
3345 # This is an individual struct
3346 else:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003347 checkExpr.append('skipCall |= validate_struct_type(report_data, "{}", "{}", "{sv}", {}{vn}, {sv}, {});\n'.format(
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003348 funcPrintName, valuePrintName, prefix, valueRequired, vn=value.name, sv=stype.value))
3349 return checkExpr
3350 #
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003351 # Generate the handle check string
Dustin Graves20fd66f2016-04-18 18:33:21 -06003352 def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName):
3353 checkExpr = []
3354 if lenValue:
Dustin Graves20fd66f2016-04-18 18:33:21 -06003355 if lenValue.ispointer:
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003356 # This is assumed to be an output array with a pointer to a count value
Dustin Graves20fd66f2016-04-18 18:33:21 -06003357 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
Dustin Graves20fd66f2016-04-18 18:33:21 -06003358 else:
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003359 # This is an array with an integer count value
Dustin Graves20fd66f2016-04-18 18:33:21 -06003360 checkExpr.append('skipCall |= validate_handle_array(report_data, "{}", "{ldn}", "{dn}", {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
3361 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix))
Dustin Graves20fd66f2016-04-18 18:33:21 -06003362 else:
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003363 # This is assumed to be an output handle pointer
Dustin Graves20fd66f2016-04-18 18:33:21 -06003364 raise('Unsupported parameter validation case: Output handles are not NULL checked')
3365 return checkExpr
3366 #
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003367 # Generate check string for an array of VkFlags values
3368 def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName):
3369 checkExpr = []
3370 flagBitsName = value.type.replace('Flags', 'FlagBits')
3371 if not flagBitsName in self.flagBits:
3372 raise('Unsupported parameter validation case: array of reserved VkFlags')
3373 else:
3374 allFlags = 'All' + flagBitsName
3375 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))
3376 self.flags[value.type] = True
3377 return checkExpr
3378 #
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003379 # Generate pNext check string
3380 def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName):
3381 checkExpr = []
3382 # Generate an array of acceptable VkStructureType values for pNext
3383 extStructCount = 0
3384 extStructVar = 'NULL'
3385 extStructNames = 'NULL'
3386 if value.extstructs:
3387 structs = value.extstructs.split(',')
3388 checkExpr.append('const VkStructureType allowedStructs[] = {' + ', '.join([self.structTypes[s].value for s in structs]) + '};\n')
3389 extStructCount = 'ARRAY_SIZE(allowedStructs)'
3390 extStructVar = 'allowedStructs'
3391 extStructNames = '"' + ', '.join(structs) + '"'
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003392 checkExpr.append('skipCall |= validate_struct_pnext(report_data, "{}", "{}", {}, {}{}, {}, {});\n'.format(
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003393 funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar))
3394 return checkExpr
3395 #
3396 # Generate the pointer check string
3397 def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName):
3398 checkExpr = []
3399 if lenValue:
3400 # This is an array with a pointer to a count value
3401 if lenValue.ispointer:
3402 # If count and array parameters are optional, there will be no validation
3403 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
3404 # 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 -06003405 checkExpr.append('skipCall |= validate_array(report_data, "{}", "{ldn}", "{dn}", {pf}{ln}, {pf}{vn}, {}, {}, {});\n'.format(
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003406 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix))
3407 # This is an array with an integer count value
3408 else:
3409 # If count and array parameters are optional, there will be no validation
3410 if valueRequired == 'true' or lenValueRequired == 'true':
3411 # Arrays of strings receive special processing
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003412 validationFuncName = 'validate_array' if value.type != 'char' else 'validate_string_array'
3413 checkExpr.append('skipCall |= {}(report_data, "{}", "{ldn}", "{dn}", {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
3414 validationFuncName, funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003415 if checkExpr:
3416 if lenValue and ('->' in lenValue.name):
3417 # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
3418 checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
3419 # This is an individual struct that is not allowed to be NULL
3420 elif not value.isoptional:
3421 # Function pointers need a reinterpret_cast to void*
3422 if value.type[:4] == 'PFN_':
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003423 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 -06003424 else:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003425 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", "{}", {}{});\n'.format(funcPrintName, valuePrintName, prefix, value.name))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003426 return checkExpr
Dustin Graveseec48bf2016-03-02 18:23:29 -07003427 #
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003428 # Process struct member validation code, performing name suibstitution if required
3429 def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix):
3430 if any(token in line for token in ['{funcName}', '{valuePrefix}', '{displayNamePrefix}']):
3431 return line.format(funcName=funcName, valuePrefix=memberNamePrefix, displayNamePrefix=memberDisplayNamePrefix)
3432 return line
3433 #
3434 # Process struct validation code for inclusion in function or parent struct validation code
3435 def expandStructCode(self, lines, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output):
3436 for line in lines:
3437 if output:
3438 output[-1] += '\n'
3439 if type(line) is list:
3440 for sub in line:
3441 output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix))
3442 else:
3443 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix))
3444 return output
3445 #
3446 # Process struct pointer/array validation code, perfoeming name substitution if required
3447 def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName):
3448 expr = []
3449 expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
3450 expr.append('{')
3451 indent = self.incIndent(None)
3452 if lenValue:
3453 # Need to process all elements in the array
3454 indexName = lenValue.name.replace('Count', 'Index')
3455 expr[-1] += '\n'
3456 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
3457 expr.append(indent + '{')
3458 indent = self.incIndent(indent)
3459 # Prefix for value name to display in error message
3460 memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
3461 memberDisplayNamePrefix = '{}[i].'.format(valueDisplayName)
3462 else:
3463 memberNamePrefix = '{}{}->'.format(prefix, value.name)
3464 memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
3465 #
3466 # Expand the struct validation lines
3467 expr = self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr)
3468 #
3469 if lenValue:
3470 # Close if and for scopes
3471 indent = self.decIndent(indent)
3472 expr.append(indent + '}\n')
3473 expr.append('}\n')
3474 return expr
3475 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003476 # Generate the parameter checking code
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003477 def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
3478 lines = [] # Generated lines of code
3479 unused = [] # Unused variable names
Dustin Graveseec48bf2016-03-02 18:23:29 -07003480 for value in values:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003481 usedLines = []
Dustin Graves29148ff2016-03-23 19:44:00 -06003482 lenParam = None
3483 #
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003484 # 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 -06003485 valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003486 #
3487 # Check for NULL pointers, ignore the inout count parameters that
3488 # will be validated with their associated array
3489 if (value.ispointer or value.isstaticarray) and not value.iscount:
3490 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003491 # Parameters for function argument generation
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003492 req = 'true' # Paramerter cannot be NULL
3493 cpReq = 'true' # Count pointer cannot be NULL
3494 cvReq = 'true' # Count value cannot be 0
Dustin Graves29148ff2016-03-23 19:44:00 -06003495 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
Dustin Graveseec48bf2016-03-02 18:23:29 -07003496 #
3497 # Generate required/optional parameter strings for the pointer and count values
3498 if value.isoptional:
Dustin Graves080069b2016-04-05 13:48:15 -06003499 req = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003500 if value.len:
3501 # The parameter is an array with an explicit count parameter
3502 lenParam = self.getLenParam(values, value.len)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003503 lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003504 if lenParam.ispointer:
3505 # Count parameters that are pointers are inout
3506 if type(lenParam.isoptional) is list:
3507 if lenParam.isoptional[0]:
Dustin Graves080069b2016-04-05 13:48:15 -06003508 cpReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003509 if lenParam.isoptional[1]:
Dustin Graves080069b2016-04-05 13:48:15 -06003510 cvReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003511 else:
3512 if lenParam.isoptional:
Dustin Graves080069b2016-04-05 13:48:15 -06003513 cpReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003514 else:
3515 if lenParam.isoptional:
Dustin Graves080069b2016-04-05 13:48:15 -06003516 cvReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003517 #
3518 # If this is a pointer to a struct with an sType field, verify the type
3519 if value.type in self.structTypes:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003520 usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName)
Dustin Graves20fd66f2016-04-18 18:33:21 -06003521 # 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
3522 elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
3523 usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName)
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003524 elif value.type in self.flags and value.isconst:
3525 usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName)
Dustin Graves0d15c6e2016-04-26 16:34:10 -06003526 elif value.isbool and value.isconst:
3527 usedLines.append('skipCall |= validate_bool32_array(report_data, "{}", "{}", "{}", {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, lenParam.name, value.name, cvReq, req, pf=valuePrefix))
3528 elif value.israngedenum and value.isconst:
3529 enumRange = self.enumRanges[value.type]
3530 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 -07003531 elif value.name == 'pNext':
3532 # 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 -06003533 if not structTypeName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003534 usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003535 else:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003536 usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003537 #
Dustin Gravesb210cee2016-03-31 09:50:42 -06003538 # If this is a pointer to a struct (input), see if it contains members that need to be checked
3539 if value.type in self.validatedStructs and value.isconst:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003540 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName))
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003541 # Non-pointer types
Dustin Graves5dbb9de2016-04-25 15:54:16 -06003542 elif (value.type in self.structTypes) or (value.type in self.validatedStructs):
3543 if value.type in self.structTypes:
3544 stype = self.structTypes[value.type]
3545 usedLines.append('skipCall |= validate_struct_type(report_data, "{}", "{}", "{sv}", &({}{vn}), {sv}, false);\n'.format(
3546 funcName, valueDisplayName, valuePrefix, vn=value.name, sv=stype.value))
3547 if value.type in self.validatedStructs:
3548 memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
3549 memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
3550 usedLines.append(self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, '', []))
Dustin Graves20fd66f2016-04-18 18:33:21 -06003551 elif value.type in self.handleTypes:
3552 if not self.isHandleOptional(value, None):
3553 usedLines.append('skipCall |= validate_required_handle(report_data, "{}", "{}", {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name))
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003554 elif value.type in self.flags:
3555 flagBitsName = value.type.replace('Flags', 'FlagBits')
3556 if not flagBitsName in self.flagBits:
Dustin Graves78df8512016-04-28 17:58:59 -06003557 usedLines.append('skipCall |= validate_reserved_flags(report_data, "{}", "{}", {pf}{});\n'.format(funcName, valueDisplayName, value.name, pf=valuePrefix))
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003558 else:
3559 flagsRequired = 'false' if value.isoptional else 'true'
3560 allFlagsName = 'All' + flagBitsName
3561 usedLines.append('skipCall |= validate_flags(report_data, "{}", "{}", "{}", {}, {pf}{}, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, pf=valuePrefix))
3562 self.flags[value.type] = True
Dustin Graves29148ff2016-03-23 19:44:00 -06003563 elif value.isbool:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003564 usedLines.append('skipCall |= validate_bool32(report_data, "{}", "{}", {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name))
Dustin Graves29148ff2016-03-23 19:44:00 -06003565 elif value.israngedenum:
3566 enumRange = self.enumRanges[value.type]
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003567 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 -07003568 #
3569 # Append the parameter check to the function body for the current command
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003570 if usedLines:
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003571 # Apply special conditional checks
3572 if value.condition:
3573 usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003574 lines += usedLines
Dustin Graves842621d2016-03-03 14:17:08 -07003575 elif not value.iscount:
Dustin Graves29148ff2016-03-23 19:44:00 -06003576 # If no expression was generated for this value, it is unreferenced by the validation function, unless
3577 # it is an array count, which is indirectly referenced for array valiadation.
Dustin Graves842621d2016-03-03 14:17:08 -07003578 unused.append(value.name)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003579 return lines, unused
Dustin Graveseec48bf2016-03-02 18:23:29 -07003580 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003581 # Generate the struct member check code from the captured data
3582 def processStructMemberData(self):
3583 indent = self.incIndent(None)
3584 for struct in self.structMembers:
Dustin Graves29148ff2016-03-23 19:44:00 -06003585 #
3586 # 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 -06003587 lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003588 if lines:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003589 self.validatedStructs[struct.name] = lines
Dustin Graveseec48bf2016-03-02 18:23:29 -07003590 #
3591 # Generate the command param check code from the captured data
3592 def processCmdData(self):
3593 indent = self.incIndent(None)
3594 for command in self.commands:
Dustin Graves20fd66f2016-04-18 18:33:21 -06003595 # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
3596 startIndex = 0 if command.name == 'vkCreateInstance' else 1
3597 lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003598 if lines:
Dustin Graveseec48bf2016-03-02 18:23:29 -07003599 cmdDef = self.getCmdDef(command) + '\n'
3600 cmdDef += '{\n'
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003601 # Process unused parameters, Ignoring the first dispatch handle parameter, which is not
3602 # processed by parameter_validation (except for vkCreateInstance, which does not have a
3603 # handle as its first parameter)
3604 if unused:
Dustin Graves20fd66f2016-04-18 18:33:21 -06003605 for name in unused:
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003606 cmdDef += indent + 'UNUSED_PARAMETER({});\n'.format(name)
Dustin Graves20fd66f2016-04-18 18:33:21 -06003607 if len(unused) > 0:
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003608 cmdDef += '\n'
Dustin Graves080069b2016-04-05 13:48:15 -06003609 cmdDef += indent + 'bool skipCall = false;\n'
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003610 for line in lines:
3611 cmdDef += '\n'
3612 if type(line) is list:
3613 for sub in line:
3614 cmdDef += indent + sub
3615 else:
3616 cmdDef += indent + line
Dustin Graveseec48bf2016-03-02 18:23:29 -07003617 cmdDef += '\n'
3618 cmdDef += indent + 'return skipCall;\n'
3619 cmdDef += '}\n'
3620 self.appendSection('command', cmdDef)