blob: c17a64462a341bd4af8308e76f3b84b111b321b1 [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
Mark Lobodzinskidc3bd852016-09-06 16:12:23 -0600420# UniqueObjectsGeneratorOptions - subclass of GeneratorOptions.
421#
422# Adds options used by UniqueObjectsOutputGenerator objects during unique
423# Objects layer generation.
424#
425# Additional members
426# prefixText - list of strings to prefix generated header with
427# (usually a copyright statement + calling convention macros).
428# protectFile - True if multiple inclusion protection should be
429# generated (based on the filename) around the entire header.
430# protectFeature - True if #ifndef..#endif protection should be
431# generated around a feature interface in the header file.
432# genFuncPointers - True if function pointer typedefs should be
433# generated
434# protectProto - If conditional protection should be generated
435# around prototype declarations, set to either '#ifdef'
436# to require opt-in (#ifdef protectProtoStr) or '#ifndef'
437# to require opt-out (#ifndef protectProtoStr). Otherwise
438# set to None.
439# protectProtoStr - #ifdef/#ifndef symbol to use around prototype
440# declarations, if protectProto is set
441# apicall - string to use for the function declaration prefix,
442# such as APICALL on Windows.
443# apientry - string to use for the calling convention macro,
444# in typedefs, such as APIENTRY.
445# apientryp - string to use for the calling convention macro
446# in function pointer typedefs, such as APIENTRYP.
447# indentFuncProto - True if prototype declarations should put each
448# parameter on a separate line
449# indentFuncPointer - True if typedefed function pointers should put each
450# parameter on a separate line
451# alignFuncParam - if nonzero and parameters are being put on a
452# separate line, align parameter names at the specified column
453class UniqueObjectsGeneratorOptions(GeneratorOptions):
454 """Represents options during C interface generation for headers"""
455 def __init__(self,
456 filename = None,
457 apiname = None,
458 profile = None,
459 versions = '.*',
460 emitversions = '.*',
461 defaultExtensions = None,
462 addExtensions = None,
463 removeExtensions = None,
464 sortProcedure = regSortFeatures,
465 prefixText = "",
466 genFuncPointers = True,
467 protectFile = False,
468 protectFeature = True,
469 protectProto = None,
470 protectProtoStr = None,
471 apicall = '',
472 apientry = '',
473 apientryp = '',
474 indentFuncProto = True,
475 indentFuncPointer = False,
476 alignFuncParam = 0,
477 genDirectory = None):
478 GeneratorOptions.__init__(self, filename, apiname, profile,
479 versions, emitversions, defaultExtensions,
480 addExtensions, removeExtensions, sortProcedure)
481 self.prefixText = prefixText
482 self.genFuncPointers = genFuncPointers
483 self.protectFile = protectFile
484 self.protectFeature = protectFeature
485 self.protectProto = protectProto
486 self.protectProtoStr = protectProtoStr
487 self.apicall = apicall
488 self.apientry = apientry
489 self.apientryp = apientryp
490 self.indentFuncProto = indentFuncProto
491 self.indentFuncPointer = indentFuncPointer
492 self.alignFuncParam = alignFuncParam
493 self.genDirectory = genDirectory
Dustin Gravesdfa6acf2016-02-11 10:10:14 -0700494
Mike Stroyandee76ef2016-01-07 15:35:37 -0700495# OutputGenerator - base class for generating API interfaces.
496# Manages basic logic, logging, and output file control
497# Derived classes actually generate formatted output.
498#
499# ---- methods ----
500# OutputGenerator(errFile, warnFile, diagFile)
501# errFile, warnFile, diagFile - file handles to write errors,
502# warnings, diagnostics to. May be None to not write.
503# logMsg(level, *args) - log messages of different categories
504# level - 'error', 'warn', or 'diag'. 'error' will also
505# raise a UserWarning exception
506# *args - print()-style arguments
507# setExtMap(map) - specify a dictionary map from extension names to
508# numbers, used in creating values for extension enumerants.
509# beginFile(genOpts) - start a new interface file
510# genOpts - GeneratorOptions controlling what's generated and how
511# endFile() - finish an interface file, closing it when done
512# beginFeature(interface, emit) - write interface for a feature
513# and tag generated features as having been done.
514# interface - element for the <version> / <extension> to generate
515# emit - actually write to the header only when True
516# endFeature() - finish an interface.
517# genType(typeinfo,name) - generate interface for a type
518# typeinfo - TypeInfo for a type
519# genStruct(typeinfo,name) - generate interface for a C "struct" type.
520# typeinfo - TypeInfo for a type interpreted as a struct
521# genGroup(groupinfo,name) - generate interface for a group of enums (C "enum")
522# groupinfo - GroupInfo for a group
523# genEnum(enuminfo, name) - generate interface for an enum (constant)
524# enuminfo - EnumInfo for an enum
525# name - enum name
526# genCmd(cmdinfo) - generate interface for a command
527# cmdinfo - CmdInfo for a command
528# makeCDecls(cmd) - return C prototype and function pointer typedef for a
529# <command> Element, as a list of two strings
530# cmd - Element for the <command>
531# newline() - print a newline to the output file (utility function)
532#
533class OutputGenerator:
534 """Generate specified API interfaces in a specific style, such as a C header"""
535 def __init__(self,
536 errFile = sys.stderr,
537 warnFile = sys.stderr,
538 diagFile = sys.stdout):
539 self.outFile = None
540 self.errFile = errFile
541 self.warnFile = warnFile
542 self.diagFile = diagFile
543 # Internal state
544 self.featureName = None
545 self.genOpts = None
546 self.registry = None
547 # Used for extension enum value generation
548 self.extBase = 1000000000
549 self.extBlockSize = 1000
550 #
551 # logMsg - write a message of different categories to different
552 # destinations.
553 # level -
554 # 'diag' (diagnostic, voluminous)
555 # 'warn' (warning)
556 # 'error' (fatal error - raises exception after logging)
557 # *args - print()-style arguments to direct to corresponding log
558 def logMsg(self, level, *args):
559 """Log a message at the given level. Can be ignored or log to a file"""
560 if (level == 'error'):
561 strfile = io.StringIO()
562 write('ERROR:', *args, file=strfile)
563 if (self.errFile != None):
564 write(strfile.getvalue(), file=self.errFile)
565 raise UserWarning(strfile.getvalue())
566 elif (level == 'warn'):
567 if (self.warnFile != None):
568 write('WARNING:', *args, file=self.warnFile)
569 elif (level == 'diag'):
570 if (self.diagFile != None):
571 write('DIAG:', *args, file=self.diagFile)
572 else:
573 raise UserWarning(
574 '*** FATAL ERROR in Generator.logMsg: unknown level:' + level)
575 #
576 # enumToValue - parses and converts an <enum> tag into a value.
577 # Returns a list
578 # first element - integer representation of the value, or None
579 # if needsNum is False. The value must be a legal number
580 # if needsNum is True.
581 # second element - string representation of the value
582 # There are several possible representations of values.
583 # A 'value' attribute simply contains the value.
584 # A 'bitpos' attribute defines a value by specifying the bit
585 # position which is set in that value.
586 # A 'offset','extbase','extends' triplet specifies a value
587 # as an offset to a base value defined by the specified
588 # 'extbase' extension name, which is then cast to the
589 # typename specified by 'extends'. This requires probing
590 # the registry database, and imbeds knowledge of the
591 # Vulkan extension enum scheme in this function.
592 def enumToValue(self, elem, needsNum):
593 name = elem.get('name')
594 numVal = None
595 if ('value' in elem.keys()):
596 value = elem.get('value')
597 # print('About to translate value =', value, 'type =', type(value))
598 if (needsNum):
599 numVal = int(value, 0)
600 # If there's a non-integer, numeric 'type' attribute (e.g. 'u' or
601 # 'ull'), append it to the string value.
602 # t = enuminfo.elem.get('type')
603 # if (t != None and t != '' and t != 'i' and t != 's'):
604 # value += enuminfo.type
605 self.logMsg('diag', 'Enum', name, '-> value [', numVal, ',', value, ']')
606 return [numVal, value]
607 if ('bitpos' in elem.keys()):
608 value = elem.get('bitpos')
609 numVal = int(value, 0)
610 numVal = 1 << numVal
611 value = '0x%08x' % numVal
612 self.logMsg('diag', 'Enum', name, '-> bitpos [', numVal, ',', value, ']')
613 return [numVal, value]
614 if ('offset' in elem.keys()):
615 # Obtain values in the mapping from the attributes
616 enumNegative = False
617 offset = int(elem.get('offset'),0)
618 extnumber = int(elem.get('extnumber'),0)
619 extends = elem.get('extends')
620 if ('dir' in elem.keys()):
621 enumNegative = True
622 self.logMsg('diag', 'Enum', name, 'offset =', offset,
623 'extnumber =', extnumber, 'extends =', extends,
624 'enumNegative =', enumNegative)
625 # Now determine the actual enumerant value, as defined
626 # in the "Layers and Extensions" appendix of the spec.
627 numVal = self.extBase + (extnumber - 1) * self.extBlockSize + offset
628 if (enumNegative):
629 numVal = -numVal
630 value = '%d' % numVal
631 # More logic needed!
632 self.logMsg('diag', 'Enum', name, '-> offset [', numVal, ',', value, ']')
633 return [numVal, value]
634 return [None, None]
635 #
636 def beginFile(self, genOpts):
637 self.genOpts = genOpts
638 #
639 # Open specified output file. Not done in constructor since a
640 # Generator can be used without writing to a file.
641 if (self.genOpts.filename != None):
Jamie Madille6f08932016-05-04 08:17:33 -0700642 if (self.genOpts.genDirectory != None):
643 self.outFile = open(os.path.join(self.genOpts.genDirectory, self.genOpts.filename), 'w')
644 else:
645 self.outFile = open(self.genOpts.filename, 'w')
Mike Stroyandee76ef2016-01-07 15:35:37 -0700646 else:
647 self.outFile = sys.stdout
648 def endFile(self):
649 self.errFile and self.errFile.flush()
650 self.warnFile and self.warnFile.flush()
651 self.diagFile and self.diagFile.flush()
652 self.outFile.flush()
653 if (self.outFile != sys.stdout and self.outFile != sys.stderr):
654 self.outFile.close()
655 self.genOpts = None
656 #
657 def beginFeature(self, interface, emit):
658 self.emit = emit
659 self.featureName = interface.get('name')
660 # If there's an additional 'protect' attribute in the feature, save it
661 self.featureExtraProtect = interface.get('protect')
662 def endFeature(self):
663 # Derived classes responsible for emitting feature
664 self.featureName = None
665 self.featureExtraProtect = None
666 # Utility method to validate we're generating something only inside a
667 # <feature> tag
668 def validateFeature(self, featureType, featureName):
669 if (self.featureName == None):
670 raise UserWarning('Attempt to generate', featureType, name,
671 'when not in feature')
672 #
673 # Type generation
674 def genType(self, typeinfo, name):
675 self.validateFeature('type', name)
676 #
677 # Struct (e.g. C "struct" type) generation
678 def genStruct(self, typeinfo, name):
679 self.validateFeature('struct', name)
680 #
681 # Group (e.g. C "enum" type) generation
682 def genGroup(self, groupinfo, name):
683 self.validateFeature('group', name)
684 #
685 # Enumerant (really, constant) generation
686 def genEnum(self, enuminfo, name):
687 self.validateFeature('enum', name)
688 #
689 # Command generation
690 def genCmd(self, cmd, name):
691 self.validateFeature('command', name)
692 #
693 # Utility functions - turn a <proto> <name> into C-language prototype
694 # and typedef declarations for that name.
695 # name - contents of <name> tag
696 # tail - whatever text follows that tag in the Element
697 def makeProtoName(self, name, tail):
698 return self.genOpts.apientry + name + tail
699 def makeTypedefName(self, name, tail):
700 return '(' + self.genOpts.apientryp + 'PFN_' + name + tail + ')'
701 #
702 # makeCParamDecl - return a string which is an indented, formatted
703 # declaration for a <param> or <member> block (e.g. function parameter
704 # or structure/union member).
705 # param - Element (<param> or <member>) to format
706 # aligncol - if non-zero, attempt to align the nested <name> element
707 # at this column
708 def makeCParamDecl(self, param, aligncol):
709 paramdecl = ' ' + noneStr(param.text)
710 for elem in param:
711 text = noneStr(elem.text)
712 tail = noneStr(elem.tail)
713 if (elem.tag == 'name' and aligncol > 0):
714 self.logMsg('diag', 'Aligning parameter', elem.text, 'to column', self.genOpts.alignFuncParam)
715 # Align at specified column, if possible
716 paramdecl = paramdecl.rstrip()
717 oldLen = len(paramdecl)
718 paramdecl = paramdecl.ljust(aligncol)
719 newLen = len(paramdecl)
720 self.logMsg('diag', 'Adjust length of parameter decl from', oldLen, 'to', newLen, ':', paramdecl)
721 paramdecl += text + tail
722 return paramdecl
723 #
724 # getCParamTypeLength - return the length of the type field is an indented, formatted
725 # declaration for a <param> or <member> block (e.g. function parameter
726 # or structure/union member).
727 # param - Element (<param> or <member>) to identify
728 def getCParamTypeLength(self, param):
729 paramdecl = ' ' + noneStr(param.text)
730 for elem in param:
731 text = noneStr(elem.text)
732 tail = noneStr(elem.tail)
733 if (elem.tag == 'name'):
734 # Align at specified column, if possible
735 newLen = len(paramdecl.rstrip())
736 self.logMsg('diag', 'Identifying length of', elem.text, 'as', newLen)
737 paramdecl += text + tail
738 return newLen
739 #
740 # makeCDecls - return C prototype and function pointer typedef for a
741 # command, as a two-element list of strings.
742 # cmd - Element containing a <command> tag
743 def makeCDecls(self, cmd):
744 """Generate C function pointer typedef for <command> Element"""
745 proto = cmd.find('proto')
746 params = cmd.findall('param')
747 # Begin accumulating prototype and typedef strings
748 pdecl = self.genOpts.apicall
749 tdecl = 'typedef '
750 #
751 # Insert the function return type/name.
752 # For prototypes, add APIENTRY macro before the name
753 # For typedefs, add (APIENTRY *<name>) around the name and
754 # use the PFN_cmdnameproc naming convention.
755 # Done by walking the tree for <proto> element by element.
Mike Stroyan3c5a6e22016-04-05 16:40:30 -0600756 # etree has elem.text followed by (elem[i], elem[i].tail)
Mike Stroyandee76ef2016-01-07 15:35:37 -0700757 # for each child element and any following text
758 # Leading text
759 pdecl += noneStr(proto.text)
760 tdecl += noneStr(proto.text)
761 # For each child element, if it's a <name> wrap in appropriate
762 # declaration. Otherwise append its contents and tail contents.
763 for elem in proto:
764 text = noneStr(elem.text)
765 tail = noneStr(elem.tail)
766 if (elem.tag == 'name'):
767 pdecl += self.makeProtoName(text, tail)
768 tdecl += self.makeTypedefName(text, tail)
769 else:
770 pdecl += text + tail
771 tdecl += text + tail
772 # Now add the parameter declaration list, which is identical
773 # for prototypes and typedefs. Concatenate all the text from
774 # a <param> node without the tags. No tree walking required
775 # since all tags are ignored.
776 # Uses: self.indentFuncProto
777 # self.indentFuncPointer
778 # self.alignFuncParam
779 # Might be able to doubly-nest the joins, e.g.
780 # ','.join(('_'.join([l[i] for i in range(0,len(l))])
781 n = len(params)
782 # Indented parameters
783 if n > 0:
784 indentdecl = '(\n'
785 for i in range(0,n):
786 paramdecl = self.makeCParamDecl(params[i], self.genOpts.alignFuncParam)
787 if (i < n - 1):
788 paramdecl += ',\n'
789 else:
790 paramdecl += ');'
791 indentdecl += paramdecl
792 else:
793 indentdecl = '(void);'
794 # Non-indented parameters
795 paramdecl = '('
796 if n > 0:
797 for i in range(0,n):
798 paramdecl += ''.join([t for t in params[i].itertext()])
799 if (i < n - 1):
800 paramdecl += ', '
801 else:
802 paramdecl += 'void'
803 paramdecl += ");";
804 return [ pdecl + indentdecl, tdecl + paramdecl ]
805 #
806 def newline(self):
807 write('', file=self.outFile)
808
809 def setRegistry(self, registry):
810 self.registry = registry
811 #
812
813# COutputGenerator - subclass of OutputGenerator.
814# Generates C-language API interfaces.
815#
816# ---- methods ----
817# COutputGenerator(errFile, warnFile, diagFile) - args as for
818# OutputGenerator. Defines additional internal state.
819# ---- methods overriding base class ----
820# beginFile(genOpts)
821# endFile()
822# beginFeature(interface, emit)
823# endFeature()
824# genType(typeinfo,name)
825# genStruct(typeinfo,name)
826# genGroup(groupinfo,name)
827# genEnum(enuminfo, name)
828# genCmd(cmdinfo)
829class COutputGenerator(OutputGenerator):
830 """Generate specified API interfaces in a specific style, such as a C header"""
831 # This is an ordered list of sections in the header file.
832 TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
833 'group', 'bitmask', 'funcpointer', 'struct']
834 ALL_SECTIONS = TYPE_SECTIONS + ['commandPointer', 'command']
835 def __init__(self,
836 errFile = sys.stderr,
837 warnFile = sys.stderr,
838 diagFile = sys.stdout):
839 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
840 # Internal state - accumulators for different inner block text
841 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
842 #
843 def beginFile(self, genOpts):
844 OutputGenerator.beginFile(self, genOpts)
845 # C-specific
846 #
847 # Multiple inclusion protection & C++ wrappers.
848 if (genOpts.protectFile and self.genOpts.filename):
Dustin Graves3ff520c2016-03-28 16:17:38 -0600849 headerSym = re.sub('\.h', '_h_',
850 os.path.basename(self.genOpts.filename)).upper()
Mike Stroyandee76ef2016-01-07 15:35:37 -0700851 write('#ifndef', headerSym, file=self.outFile)
852 write('#define', headerSym, '1', file=self.outFile)
853 self.newline()
854 write('#ifdef __cplusplus', file=self.outFile)
855 write('extern "C" {', file=self.outFile)
856 write('#endif', file=self.outFile)
857 self.newline()
858 #
859 # User-supplied prefix text, if any (list of strings)
860 if (genOpts.prefixText):
861 for s in genOpts.prefixText:
862 write(s, file=self.outFile)
863 #
864 # Some boilerplate describing what was generated - this
865 # will probably be removed later since the extensions
866 # pattern may be very long.
867 # write('/* Generated C header for:', file=self.outFile)
868 # write(' * API:', genOpts.apiname, file=self.outFile)
869 # if (genOpts.profile):
870 # write(' * Profile:', genOpts.profile, file=self.outFile)
871 # write(' * Versions considered:', genOpts.versions, file=self.outFile)
872 # write(' * Versions emitted:', genOpts.emitversions, file=self.outFile)
873 # write(' * Default extensions included:', genOpts.defaultExtensions, file=self.outFile)
874 # write(' * Additional extensions included:', genOpts.addExtensions, file=self.outFile)
875 # write(' * Extensions removed:', genOpts.removeExtensions, file=self.outFile)
876 # write(' */', file=self.outFile)
877 def endFile(self):
878 # C-specific
879 # Finish C++ wrapper and multiple inclusion protection
880 self.newline()
881 write('#ifdef __cplusplus', file=self.outFile)
882 write('}', file=self.outFile)
883 write('#endif', file=self.outFile)
884 if (self.genOpts.protectFile and self.genOpts.filename):
885 self.newline()
886 write('#endif', file=self.outFile)
887 # Finish processing in superclass
888 OutputGenerator.endFile(self)
889 def beginFeature(self, interface, emit):
890 # Start processing in superclass
891 OutputGenerator.beginFeature(self, interface, emit)
892 # C-specific
893 # Accumulate includes, defines, types, enums, function pointer typedefs,
894 # end function prototypes separately for this feature. They're only
895 # printed in endFeature().
896 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
897 def endFeature(self):
898 # C-specific
899 # Actually write the interface to the output file.
900 if (self.emit):
901 self.newline()
902 if (self.genOpts.protectFeature):
903 write('#ifndef', self.featureName, file=self.outFile)
904 # If type declarations are needed by other features based on
905 # this one, it may be necessary to suppress the ExtraProtect,
906 # or move it below the 'for section...' loop.
907 if (self.featureExtraProtect != None):
908 write('#ifdef', self.featureExtraProtect, file=self.outFile)
909 write('#define', self.featureName, '1', file=self.outFile)
910 for section in self.TYPE_SECTIONS:
911 contents = self.sections[section]
912 if contents:
913 write('\n'.join(contents), file=self.outFile)
914 self.newline()
915 if (self.genOpts.genFuncPointers and self.sections['commandPointer']):
916 write('\n'.join(self.sections['commandPointer']), file=self.outFile)
917 self.newline()
918 if (self.sections['command']):
919 if (self.genOpts.protectProto):
920 write(self.genOpts.protectProto,
921 self.genOpts.protectProtoStr, file=self.outFile)
922 write('\n'.join(self.sections['command']), end='', file=self.outFile)
923 if (self.genOpts.protectProto):
924 write('#endif', file=self.outFile)
925 else:
926 self.newline()
927 if (self.featureExtraProtect != None):
928 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
929 if (self.genOpts.protectFeature):
930 write('#endif /*', self.featureName, '*/', file=self.outFile)
931 # Finish processing in superclass
932 OutputGenerator.endFeature(self)
933 #
934 # Append a definition to the specified section
935 def appendSection(self, section, text):
936 # self.sections[section].append('SECTION: ' + section + '\n')
937 self.sections[section].append(text)
938 #
939 # Type generation
940 def genType(self, typeinfo, name):
941 OutputGenerator.genType(self, typeinfo, name)
942 typeElem = typeinfo.elem
943 # If the type is a struct type, traverse the imbedded <member> tags
944 # generating a structure. Otherwise, emit the tag text.
945 category = typeElem.get('category')
946 if (category == 'struct' or category == 'union'):
947 self.genStruct(typeinfo, name)
948 else:
949 # Replace <apientry /> tags with an APIENTRY-style string
950 # (from self.genOpts). Copy other text through unchanged.
951 # If the resulting text is an empty string, don't emit it.
952 s = noneStr(typeElem.text)
953 for elem in typeElem:
954 if (elem.tag == 'apientry'):
955 s += self.genOpts.apientry + noneStr(elem.tail)
956 else:
957 s += noneStr(elem.text) + noneStr(elem.tail)
958 if s:
959 # Add extra newline after multi-line entries.
960 if '\n' in s:
961 s += '\n'
962 self.appendSection(category, s)
963 #
964 # Struct (e.g. C "struct" type) generation.
965 # This is a special case of the <type> tag where the contents are
966 # interpreted as a set of <member> tags instead of freeform C
967 # C type declarations. The <member> tags are just like <param>
968 # tags - they are a declaration of a struct or union member.
969 # Only simple member declarations are supported (no nested
970 # structs etc.)
971 def genStruct(self, typeinfo, typeName):
972 OutputGenerator.genStruct(self, typeinfo, typeName)
973 body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
974 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
975 targetLen = 0;
976 for member in typeinfo.elem.findall('.//member'):
977 targetLen = max(targetLen, self.getCParamTypeLength(member))
978 for member in typeinfo.elem.findall('.//member'):
979 body += self.makeCParamDecl(member, targetLen + 4)
980 body += ';\n'
981 body += '} ' + typeName + ';\n'
982 self.appendSection('struct', body)
983 #
984 # Group (e.g. C "enum" type) generation.
985 # These are concatenated together with other types.
986 def genGroup(self, groupinfo, groupName):
987 OutputGenerator.genGroup(self, groupinfo, groupName)
988 groupElem = groupinfo.elem
Dustin Graves3ff520c2016-03-28 16:17:38 -0600989
990 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
991
992 expandPrefix = expandName
993 expandSuffix = ''
994 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
995 if expandSuffixMatch:
996 expandSuffix = '_' + expandSuffixMatch.group()
997 # Strip off the suffix from the prefix
998 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
999
Mike Stroyandee76ef2016-01-07 15:35:37 -07001000 # Prefix
1001 body = "\ntypedef enum " + groupName + " {\n"
1002
Dustin Graves3ff520c2016-03-28 16:17:38 -06001003 isEnum = ('FLAG_BITS' not in expandPrefix)
1004
Mike Stroyandee76ef2016-01-07 15:35:37 -07001005 # Loop over the nested 'enum' tags. Keep track of the minimum and
1006 # maximum numeric values, if they can be determined; but only for
1007 # core API enumerants, not extension enumerants. This is inferred
1008 # by looking for 'extends' attributes.
1009 minName = None
1010 for elem in groupElem.findall('enum'):
1011 # Convert the value to an integer and use that to track min/max.
1012 # Values of form -(number) are accepted but nothing more complex.
1013 # Should catch exceptions here for more complex constructs. Not yet.
1014 (numVal,strVal) = self.enumToValue(elem, True)
1015 name = elem.get('name')
Dustin Graves3ff520c2016-03-28 16:17:38 -06001016
1017 # Extension enumerants are only included if they are requested
1018 # in addExtensions or match defaultExtensions.
1019 if (elem.get('extname') is None or
1020 re.match(self.genOpts.addExtensions,elem.get('extname')) is not None or
1021 self.genOpts.defaultExtensions == elem.get('supported')):
1022 body += " " + name + " = " + strVal + ",\n"
1023
1024 if (isEnum and elem.get('extends') is None):
Mike Stroyandee76ef2016-01-07 15:35:37 -07001025 if (minName == None):
1026 minName = maxName = name
1027 minValue = maxValue = numVal
1028 elif (numVal < minValue):
1029 minName = name
1030 minValue = numVal
1031 elif (numVal > maxValue):
1032 maxName = name
1033 maxValue = numVal
1034 # Generate min/max value tokens and a range-padding enum. Need some
1035 # additional padding to generate correct names...
Dustin Graves3ff520c2016-03-28 16:17:38 -06001036 if isEnum:
1037 body += " " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n"
1038 body += " " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n"
1039 body += " " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n"
1040
1041 body += " " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
1042
Mike Stroyandee76ef2016-01-07 15:35:37 -07001043 # Postfix
1044 body += "} " + groupName + ";"
1045 if groupElem.get('type') == 'bitmask':
1046 section = 'bitmask'
1047 else:
1048 section = 'group'
1049 self.appendSection(section, body)
1050 # Enumerant generation
1051 # <enum> tags may specify their values in several ways, but are usually
1052 # just integers.
1053 def genEnum(self, enuminfo, name):
1054 OutputGenerator.genEnum(self, enuminfo, name)
1055 (numVal,strVal) = self.enumToValue(enuminfo.elem, False)
1056 body = '#define ' + name.ljust(33) + ' ' + strVal
1057 self.appendSection('enum', body)
1058 #
1059 # Command generation
1060 def genCmd(self, cmdinfo, name):
1061 OutputGenerator.genCmd(self, cmdinfo, name)
1062 #
1063 decls = self.makeCDecls(cmdinfo.elem)
1064 self.appendSection('command', decls[0] + '\n')
1065 if (self.genOpts.genFuncPointers):
1066 self.appendSection('commandPointer', decls[1])
1067
1068# DocOutputGenerator - subclass of OutputGenerator.
1069# Generates AsciiDoc includes with C-language API interfaces, for reference
1070# pages and the Vulkan specification. Similar to COutputGenerator, but
1071# each interface is written into a different file as determined by the
1072# options, only actual C types are emitted, and none of the boilerplate
1073# preprocessor code is emitted.
1074#
1075# ---- methods ----
1076# DocOutputGenerator(errFile, warnFile, diagFile) - args as for
1077# OutputGenerator. Defines additional internal state.
1078# ---- methods overriding base class ----
1079# beginFile(genOpts)
1080# endFile()
1081# beginFeature(interface, emit)
1082# endFeature()
1083# genType(typeinfo,name)
1084# genStruct(typeinfo,name)
1085# genGroup(groupinfo,name)
1086# genEnum(enuminfo, name)
1087# genCmd(cmdinfo)
1088class DocOutputGenerator(OutputGenerator):
1089 """Generate specified API interfaces in a specific style, such as a C header"""
1090 def __init__(self,
1091 errFile = sys.stderr,
1092 warnFile = sys.stderr,
1093 diagFile = sys.stdout):
1094 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1095 #
1096 def beginFile(self, genOpts):
1097 OutputGenerator.beginFile(self, genOpts)
1098 def endFile(self):
1099 OutputGenerator.endFile(self)
1100 def beginFeature(self, interface, emit):
1101 # Start processing in superclass
1102 OutputGenerator.beginFeature(self, interface, emit)
1103 def endFeature(self):
1104 # Finish processing in superclass
1105 OutputGenerator.endFeature(self)
1106 #
1107 # Generate an include file
1108 #
1109 # directory - subdirectory to put file in
1110 # basename - base name of the file
1111 # contents - contents of the file (Asciidoc boilerplate aside)
1112 def writeInclude(self, directory, basename, contents):
1113 # Create file
1114 filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
1115 self.logMsg('diag', '# Generating include file:', filename)
1116 fp = open(filename, 'w')
1117 # Asciidoc anchor
Dustin Graves3ff520c2016-03-28 16:17:38 -06001118 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
1119 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001120 write('[[{0},{0}]]'.format(basename), file=fp)
1121 write('["source","{basebackend@docbook:c++:cpp}",title=""]', file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001122 write('endif::doctype-manpage[]', file=fp)
1123 write('ifdef::doctype-manpage[]', file=fp)
1124 write('["source","{basebackend@docbook:c++:cpp}"]', file=fp)
1125 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001126 write('------------------------------------------------------------------------------', file=fp)
1127 write(contents, file=fp)
1128 write('------------------------------------------------------------------------------', file=fp)
1129 fp.close()
1130 #
1131 # Type generation
1132 def genType(self, typeinfo, name):
1133 OutputGenerator.genType(self, typeinfo, name)
1134 typeElem = typeinfo.elem
1135 # If the type is a struct type, traverse the imbedded <member> tags
1136 # generating a structure. Otherwise, emit the tag text.
1137 category = typeElem.get('category')
1138 if (category == 'struct' or category == 'union'):
1139 self.genStruct(typeinfo, name)
1140 else:
1141 # Replace <apientry /> tags with an APIENTRY-style string
1142 # (from self.genOpts). Copy other text through unchanged.
1143 # If the resulting text is an empty string, don't emit it.
1144 s = noneStr(typeElem.text)
1145 for elem in typeElem:
1146 if (elem.tag == 'apientry'):
1147 s += self.genOpts.apientry + noneStr(elem.tail)
1148 else:
1149 s += noneStr(elem.text) + noneStr(elem.tail)
1150 if (len(s) > 0):
1151 if (category == 'bitmask'):
1152 self.writeInclude('flags', name, s + '\n')
1153 elif (category == 'enum'):
1154 self.writeInclude('enums', name, s + '\n')
1155 elif (category == 'funcpointer'):
1156 self.writeInclude('funcpointers', name, s+ '\n')
1157 else:
1158 self.logMsg('diag', '# NOT writing include file for type:',
1159 name, 'category: ', category)
1160 else:
1161 self.logMsg('diag', '# NOT writing empty include file for type', name)
1162 #
1163 # Struct (e.g. C "struct" type) generation.
1164 # This is a special case of the <type> tag where the contents are
1165 # interpreted as a set of <member> tags instead of freeform C
1166 # C type declarations. The <member> tags are just like <param>
1167 # tags - they are a declaration of a struct or union member.
1168 # Only simple member declarations are supported (no nested
1169 # structs etc.)
1170 def genStruct(self, typeinfo, typeName):
1171 OutputGenerator.genStruct(self, typeinfo, typeName)
1172 s = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
1173 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
1174 targetLen = 0;
1175 for member in typeinfo.elem.findall('.//member'):
1176 targetLen = max(targetLen, self.getCParamTypeLength(member))
1177 for member in typeinfo.elem.findall('.//member'):
1178 s += self.makeCParamDecl(member, targetLen + 4)
1179 s += ';\n'
1180 s += '} ' + typeName + ';'
1181 self.writeInclude('structs', typeName, s)
1182 #
1183 # Group (e.g. C "enum" type) generation.
1184 # These are concatenated together with other types.
1185 def genGroup(self, groupinfo, groupName):
1186 OutputGenerator.genGroup(self, groupinfo, groupName)
1187 groupElem = groupinfo.elem
Dustin Graves3ff520c2016-03-28 16:17:38 -06001188
1189 # See if we need min/max/num/padding at end
1190 expand = self.genOpts.expandEnumerants
1191
1192 if expand:
1193 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
1194 isEnum = ('FLAG_BITS' not in expandName)
1195
1196 expandPrefix = expandName
1197 expandSuffix = ''
1198
1199 # Look for a suffix
1200 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
1201 if expandSuffixMatch:
1202 expandSuffix = '_' + expandSuffixMatch.group()
1203 # Strip off the suffix from the prefix
1204 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
1205
Mike Stroyandee76ef2016-01-07 15:35:37 -07001206 # Prefix
1207 s = "typedef enum " + groupName + " {\n"
1208
1209 # Loop over the nested 'enum' tags. Keep track of the minimum and
1210 # maximum numeric values, if they can be determined.
1211 minName = None
1212 for elem in groupElem.findall('enum'):
1213 # Convert the value to an integer and use that to track min/max.
1214 # Values of form -(number) are accepted but nothing more complex.
1215 # Should catch exceptions here for more complex constructs. Not yet.
1216 (numVal,strVal) = self.enumToValue(elem, True)
1217 name = elem.get('name')
Dustin Graves3ff520c2016-03-28 16:17:38 -06001218
1219 # Extension enumerants are only included if they are requested
1220 # in addExtensions or match defaultExtensions.
1221 if (elem.get('extname') is None or
1222 re.match(self.genOpts.addExtensions,elem.get('extname')) is not None or
1223 self.genOpts.defaultExtensions == elem.get('supported')):
1224 s += " " + name + " = " + strVal + ",\n"
1225
1226 if (expand and isEnum and elem.get('extends') is None):
Mike Stroyandee76ef2016-01-07 15:35:37 -07001227 if (minName == None):
1228 minName = maxName = name
1229 minValue = maxValue = numVal
1230 elif (numVal < minValue):
1231 minName = name
1232 minValue = numVal
1233 elif (numVal > maxValue):
1234 maxName = name
1235 maxValue = numVal
1236 # Generate min/max value tokens and a range-padding enum. Need some
1237 # additional padding to generate correct names...
1238 if (expand):
1239 s += "\n"
Dustin Graves3ff520c2016-03-28 16:17:38 -06001240 if isEnum:
1241 s += " " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n"
1242 s += " " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n"
1243 s += " " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n"
1244
1245 s += " " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
Mike Stroyandee76ef2016-01-07 15:35:37 -07001246 # Postfix
1247 s += "} " + groupName + ";"
1248 self.writeInclude('enums', groupName, s)
1249 # Enumerant generation
1250 # <enum> tags may specify their values in several ways, but are usually
1251 # just integers.
1252 def genEnum(self, enuminfo, name):
1253 OutputGenerator.genEnum(self, enuminfo, name)
1254 (numVal,strVal) = self.enumToValue(enuminfo.elem, False)
1255 s = '#define ' + name.ljust(33) + ' ' + strVal
1256 self.logMsg('diag', '# NOT writing compile-time constant', name)
1257 # self.writeInclude('consts', name, s)
1258 #
1259 # Command generation
1260 def genCmd(self, cmdinfo, name):
1261 OutputGenerator.genCmd(self, cmdinfo, name)
1262 #
1263 decls = self.makeCDecls(cmdinfo.elem)
1264 self.writeInclude('protos', name, decls[0])
1265
1266# PyOutputGenerator - subclass of OutputGenerator.
1267# Generates Python data structures describing API names.
1268# Similar to DocOutputGenerator, but writes a single
1269# file.
1270#
1271# ---- methods ----
1272# PyOutputGenerator(errFile, warnFile, diagFile) - args as for
1273# OutputGenerator. Defines additional internal state.
1274# ---- methods overriding base class ----
1275# beginFile(genOpts)
1276# endFile()
1277# genType(typeinfo,name)
1278# genStruct(typeinfo,name)
1279# genGroup(groupinfo,name)
1280# genEnum(enuminfo, name)
1281# genCmd(cmdinfo)
1282class PyOutputGenerator(OutputGenerator):
1283 """Generate specified API interfaces in a specific style, such as a C header"""
1284 def __init__(self,
1285 errFile = sys.stderr,
1286 warnFile = sys.stderr,
1287 diagFile = sys.stdout):
1288 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1289 #
1290 def beginFile(self, genOpts):
1291 OutputGenerator.beginFile(self, genOpts)
1292 for dict in [ 'flags', 'enums', 'structs', 'consts', 'enums',
1293 'consts', 'protos', 'funcpointers' ]:
1294 write(dict, '= {}', file=self.outFile)
1295 def endFile(self):
1296 OutputGenerator.endFile(self)
1297 #
1298 # Add a name from the interface
1299 #
1300 # dict - type of name (see beginFile above)
1301 # name - name to add
1302 # value - A serializable Python value for the name
1303 def addName(self, dict, name, value=None):
1304 write(dict + "['" + name + "'] = ", value, file=self.outFile)
1305 #
1306 # Type generation
1307 # For 'struct' or 'union' types, defer to genStruct() to
1308 # add to the dictionary.
1309 # For 'bitmask' types, add the type name to the 'flags' dictionary,
1310 # with the value being the corresponding 'enums' name defining
1311 # the acceptable flag bits.
1312 # For 'enum' types, add the type name to the 'enums' dictionary,
1313 # with the value being '@STOPHERE@' (because this case seems
1314 # never to happen).
1315 # For 'funcpointer' types, add the type name to the 'funcpointers'
1316 # dictionary.
1317 # For 'handle' and 'define' types, add the handle or #define name
1318 # to the 'struct' dictionary, because that's how the spec sources
1319 # tag these types even though they aren't structs.
1320 def genType(self, typeinfo, name):
1321 OutputGenerator.genType(self, typeinfo, name)
1322 typeElem = typeinfo.elem
1323 # If the type is a struct type, traverse the imbedded <member> tags
1324 # generating a structure. Otherwise, emit the tag text.
1325 category = typeElem.get('category')
1326 if (category == 'struct' or category == 'union'):
1327 self.genStruct(typeinfo, name)
1328 else:
1329 # Extract the type name
1330 # (from self.genOpts). Copy other text through unchanged.
1331 # If the resulting text is an empty string, don't emit it.
1332 count = len(noneStr(typeElem.text))
1333 for elem in typeElem:
1334 count += len(noneStr(elem.text)) + len(noneStr(elem.tail))
1335 if (count > 0):
1336 if (category == 'bitmask'):
1337 requiredEnum = typeElem.get('requires')
1338 self.addName('flags', name, enquote(requiredEnum))
1339 elif (category == 'enum'):
1340 # This case never seems to come up!
1341 # @enums C 'enum' name Dictionary of enumerant names
1342 self.addName('enums', name, enquote('@STOPHERE@'))
1343 elif (category == 'funcpointer'):
1344 self.addName('funcpointers', name, None)
1345 elif (category == 'handle' or category == 'define'):
1346 self.addName('structs', name, None)
1347 else:
1348 write('# Unprocessed type:', name, 'category:', category, file=self.outFile)
1349 else:
1350 write('# Unprocessed type:', name, file=self.outFile)
1351 #
1352 # Struct (e.g. C "struct" type) generation.
1353 #
1354 # Add the struct name to the 'structs' dictionary, with the
1355 # value being an ordered list of the struct member names.
1356 def genStruct(self, typeinfo, typeName):
1357 OutputGenerator.genStruct(self, typeinfo, typeName)
1358
1359 members = [member.text for member in typeinfo.elem.findall('.//member/name')]
1360 self.addName('structs', typeName, members)
1361 #
1362 # Group (e.g. C "enum" type) generation.
1363 # These are concatenated together with other types.
1364 #
1365 # Add the enum type name to the 'enums' dictionary, with
1366 # the value being an ordered list of the enumerant names.
1367 # Add each enumerant name to the 'consts' dictionary, with
1368 # the value being the enum type the enumerant is part of.
1369 def genGroup(self, groupinfo, groupName):
1370 OutputGenerator.genGroup(self, groupinfo, groupName)
1371 groupElem = groupinfo.elem
1372
1373 # @enums C 'enum' name Dictionary of enumerant names
1374 # @consts C enumerant/const name Name of corresponding 'enums' key
1375
1376 # Loop over the nested 'enum' tags. Keep track of the minimum and
1377 # maximum numeric values, if they can be determined.
1378 enumerants = [elem.get('name') for elem in groupElem.findall('enum')]
1379 for name in enumerants:
1380 self.addName('consts', name, enquote(groupName))
1381 self.addName('enums', groupName, enumerants)
1382 # Enumerant generation (compile-time constants)
1383 #
1384 # Add the constant name to the 'consts' dictionary, with the
1385 # value being None to indicate that the constant isn't
1386 # an enumeration value.
1387 def genEnum(self, enuminfo, name):
1388 OutputGenerator.genEnum(self, enuminfo, name)
1389
1390 # @consts C enumerant/const name Name of corresponding 'enums' key
1391
1392 self.addName('consts', name, None)
1393 #
1394 # Command generation
1395 #
1396 # Add the command name to the 'protos' dictionary, with the
1397 # value being an ordered list of the parameter names.
1398 def genCmd(self, cmdinfo, name):
1399 OutputGenerator.genCmd(self, cmdinfo, name)
1400
1401 params = [param.text for param in cmdinfo.elem.findall('param/name')]
1402 self.addName('protos', name, params)
1403
1404# ValidityOutputGenerator - subclass of OutputGenerator.
1405# Generates AsciiDoc includes of valid usage information, for reference
1406# pages and the Vulkan specification. Similar to DocOutputGenerator.
1407#
1408# ---- methods ----
1409# ValidityOutputGenerator(errFile, warnFile, diagFile) - args as for
1410# OutputGenerator. Defines additional internal state.
1411# ---- methods overriding base class ----
1412# beginFile(genOpts)
1413# endFile()
1414# beginFeature(interface, emit)
1415# endFeature()
1416# genCmd(cmdinfo)
1417class ValidityOutputGenerator(OutputGenerator):
1418 """Generate specified API interfaces in a specific style, such as a C header"""
1419 def __init__(self,
1420 errFile = sys.stderr,
1421 warnFile = sys.stderr,
1422 diagFile = sys.stdout):
1423 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1424
1425 def beginFile(self, genOpts):
1426 OutputGenerator.beginFile(self, genOpts)
1427 def endFile(self):
1428 OutputGenerator.endFile(self)
1429 def beginFeature(self, interface, emit):
1430 # Start processing in superclass
1431 OutputGenerator.beginFeature(self, interface, emit)
1432 def endFeature(self):
1433 # Finish processing in superclass
1434 OutputGenerator.endFeature(self)
1435
1436 def makeParameterName(self, name):
1437 return 'pname:' + name
1438
1439 def makeStructName(self, name):
1440 return 'sname:' + name
1441
1442 def makeBaseTypeName(self, name):
1443 return 'basetype:' + name
1444
1445 def makeEnumerationName(self, name):
1446 return 'elink:' + name
1447
1448 def makeEnumerantName(self, name):
1449 return 'ename:' + name
1450
1451 def makeFLink(self, name):
1452 return 'flink:' + name
1453
1454 #
1455 # Generate an include file
1456 #
1457 # directory - subdirectory to put file in
1458 # basename - base name of the file
1459 # contents - contents of the file (Asciidoc boilerplate aside)
1460 def writeInclude(self, directory, basename, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes):
1461 # Create file
1462 filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
1463 self.logMsg('diag', '# Generating include file:', filename)
1464 fp = open(filename, 'w')
1465 # Asciidoc anchor
Dustin Graves3ff520c2016-03-28 16:17:38 -06001466 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001467
1468 # Valid Usage
1469 if validity is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001470 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001471 write('.Valid Usage', file=fp)
1472 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001473 write('endif::doctype-manpage[]', file=fp)
1474 write('ifdef::doctype-manpage[]', file=fp)
1475 write('Valid Usage', file=fp)
1476 write('-----------', file=fp)
1477 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001478 write(validity, file=fp, end='')
Dustin Graves3ff520c2016-03-28 16:17:38 -06001479 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001480 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001481 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001482 write('', file=fp)
1483
1484 # Host Synchronization
1485 if threadsafety is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001486 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001487 write('.Host Synchronization', file=fp)
1488 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001489 write('endif::doctype-manpage[]', file=fp)
1490 write('ifdef::doctype-manpage[]', file=fp)
1491 write('Host Synchronization', file=fp)
1492 write('--------------------', file=fp)
1493 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001494 write(threadsafety, file=fp, end='')
Dustin Graves3ff520c2016-03-28 16:17:38 -06001495 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001496 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001497 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001498 write('', file=fp)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07001499
Mike Stroyandee76ef2016-01-07 15:35:37 -07001500 # Command Properties - contained within a block, to avoid table numbering
1501 if commandpropertiesentry is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001502 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001503 write('.Command Properties', file=fp)
1504 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001505 write('endif::doctype-manpage[]', file=fp)
1506 write('ifdef::doctype-manpage[]', file=fp)
1507 write('Command Properties', file=fp)
1508 write('------------------', file=fp)
1509 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001510 write('[options="header", width="100%"]', file=fp)
1511 write('|=====================', file=fp)
1512 write('|Command Buffer Levels|Render Pass Scope|Supported Queue Types', file=fp)
1513 write(commandpropertiesentry, file=fp)
1514 write('|=====================', file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001515 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001516 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001517 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001518 write('', file=fp)
1519
1520 # Success Codes - contained within a block, to avoid table numbering
1521 if successcodes is not None or errorcodes is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001522 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001523 write('.Return Codes', file=fp)
1524 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001525 write('endif::doctype-manpage[]', file=fp)
1526 write('ifdef::doctype-manpage[]', file=fp)
1527 write('Return Codes', file=fp)
1528 write('------------', file=fp)
1529 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001530 if successcodes is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001531 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001532 write('<<fundamentals-successcodes,Success>>::', file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001533 write('endif::doctype-manpage[]', file=fp)
1534 write('ifdef::doctype-manpage[]', file=fp)
1535 write('On success, this command returns::', file=fp)
1536 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001537 write(successcodes, file=fp)
1538 if errorcodes is not None:
Dustin Graves3ff520c2016-03-28 16:17:38 -06001539 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001540 write('<<fundamentals-errorcodes,Failure>>::', file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001541 write('endif::doctype-manpage[]', file=fp)
1542 write('ifdef::doctype-manpage[]', file=fp)
1543 write('On failure, this command returns::', file=fp)
1544 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001545 write(errorcodes, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001546 write('ifndef::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001547 write('*' * 80, file=fp)
Dustin Graves3ff520c2016-03-28 16:17:38 -06001548 write('endif::doctype-manpage[]', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07001549 write('', file=fp)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07001550
Mike Stroyandee76ef2016-01-07 15:35:37 -07001551 fp.close()
1552
1553 #
1554 # Check if the parameter passed in is a pointer
1555 def paramIsPointer(self, param):
1556 ispointer = False
1557 paramtype = param.find('type')
1558 if paramtype.tail is not None and '*' in paramtype.tail:
1559 ispointer = True
1560
1561 return ispointer
1562
1563 #
1564 # Check if the parameter passed in is a static array
1565 def paramIsStaticArray(self, param):
1566 if param.find('name').tail is not None:
1567 if param.find('name').tail[0] == '[':
1568 return True
1569
1570 #
1571 # Get the length of a parameter that's been identified as a static array
1572 def staticArrayLength(self, param):
1573 paramname = param.find('name')
1574 paramenumsize = param.find('enum')
1575
1576 if paramenumsize is not None:
1577 return paramenumsize.text
1578 else:
1579 return paramname.tail[1:-1]
1580
1581 #
1582 # Check if the parameter passed in is a pointer to an array
1583 def paramIsArray(self, param):
1584 return param.attrib.get('len') is not None
1585
1586 #
1587 # Get the parent of a handle object
1588 def getHandleParent(self, typename):
1589 types = self.registry.findall("types/type")
1590 for elem in types:
1591 if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
1592 return elem.attrib.get('parent')
1593
1594 #
1595 # Check if a parent object is dispatchable or not
1596 def isHandleTypeDispatchable(self, handlename):
1597 handle = self.registry.find("types/type/[name='" + handlename + "'][@category='handle']")
1598 if handle is not None and handle.find('type').text == 'VK_DEFINE_HANDLE':
1599 return True
1600 else:
1601 return False
1602
1603 def isHandleOptional(self, param, params):
1604
1605 # See if the handle is optional
1606 isOptional = False
1607
1608 # Simple, if it's optional, return true
1609 if param.attrib.get('optional') is not None:
1610 return True
1611
1612 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
1613 if param.attrib.get('noautovalidity') is not None:
1614 return True
1615
1616 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
1617 if self.paramIsArray(param):
1618 lengths = param.attrib.get('len').split(',')
1619 for length in lengths:
1620 if (length) != 'null-terminated' and (length) != '1':
1621 for otherparam in params:
1622 if otherparam.find('name').text == length:
1623 if otherparam.attrib.get('optional') is not None:
1624 return True
1625
1626 return False
1627 #
1628 # Get the category of a type
1629 def getTypeCategory(self, typename):
1630 types = self.registry.findall("types/type")
1631 for elem in types:
1632 if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
1633 return elem.attrib.get('category')
1634
1635 #
1636 # Make a chunk of text for the end of a parameter if it is an array
1637 def makeAsciiDocPreChunk(self, param, params):
1638 paramname = param.find('name')
1639 paramtype = param.find('type')
1640
1641 # General pre-amble. Check optionality and add stuff.
1642 asciidoc = '* '
1643
1644 if self.paramIsStaticArray(param):
1645 asciidoc += 'Any given element of '
1646
1647 elif self.paramIsArray(param):
1648 lengths = param.attrib.get('len').split(',')
1649
1650 # 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
1651 optionallengths = []
1652 for length in lengths:
1653 if (length) != 'null-terminated' and (length) != '1':
1654 for otherparam in params:
1655 if otherparam.find('name').text == length:
1656 if otherparam.attrib.get('optional') is not None:
1657 if self.paramIsPointer(otherparam):
1658 optionallengths.append('the value referenced by ' + self.makeParameterName(length))
1659 else:
1660 optionallengths.append(self.makeParameterName(length))
1661
1662 # Document that these arrays may be ignored if any of the length values are 0
1663 if len(optionallengths) != 0 or param.attrib.get('optional') is not None:
1664 asciidoc += 'If '
1665
1666
1667 if len(optionallengths) != 0:
1668 if len(optionallengths) == 1:
1669
1670 asciidoc += optionallengths[0]
1671 asciidoc += ' is '
1672
1673 else:
1674 asciidoc += ' or '.join(optionallengths)
1675 asciidoc += ' are '
1676
1677 asciidoc += 'not `0`, '
1678
1679 if len(optionallengths) != 0 and param.attrib.get('optional') is not None:
1680 asciidoc += 'and '
1681
1682 if param.attrib.get('optional') is not None:
1683 asciidoc += self.makeParameterName(paramname.text)
1684 asciidoc += ' is not `NULL`, '
1685
1686 elif param.attrib.get('optional') is not None:
1687 # Don't generate this stub for bitflags
1688 if self.getTypeCategory(paramtype.text) != 'bitmask':
1689 if param.attrib.get('optional').split(',')[0] == 'true':
1690 asciidoc += 'If '
1691 asciidoc += self.makeParameterName(paramname.text)
1692 asciidoc += ' is not '
1693 if self.paramIsArray(param) or self.paramIsPointer(param) or self.isHandleTypeDispatchable(paramtype.text):
1694 asciidoc += '`NULL`'
1695 elif self.getTypeCategory(paramtype.text) == 'handle':
1696 asciidoc += 'sname:VK_NULL_HANDLE'
1697 else:
1698 asciidoc += '`0`'
1699
1700 asciidoc += ', '
1701
1702 return asciidoc
1703
1704 #
1705 # Make the generic asciidoc line chunk portion used for all parameters.
1706 # May return an empty string if nothing to validate.
1707 def createValidationLineForParameterIntroChunk(self, param, params, typetext):
1708 asciidoc = ''
1709 paramname = param.find('name')
1710 paramtype = param.find('type')
1711
1712 asciidoc += self.makeAsciiDocPreChunk(param, params)
1713
1714 asciidoc += self.makeParameterName(paramname.text)
1715 asciidoc += ' must: be '
1716
1717 if self.paramIsArray(param):
1718 # Arrays. These are hard to get right, apparently
1719
1720 lengths = param.attrib.get('len').split(',')
1721
1722 if (lengths[0]) == 'null-terminated':
1723 asciidoc += 'a null-terminated '
1724 elif (lengths[0]) == '1':
1725 asciidoc += 'a pointer to '
1726 else:
1727 asciidoc += 'a pointer to an array of '
1728
1729 # Handle equations, which are currently denoted with latex
1730 if 'latexmath:' in lengths[0]:
1731 asciidoc += lengths[0]
1732 else:
1733 asciidoc += self.makeParameterName(lengths[0])
1734 asciidoc += ' '
1735
1736 for length in lengths[1:]:
1737 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.
1738 asciidoc += 'null-terminated '
1739 elif (length) == '1':
1740 asciidoc += 'pointers to '
1741 else:
1742 asciidoc += 'pointers to arrays of '
1743 # Handle equations, which are currently denoted with latex
1744 if 'latex:' in length:
1745 asciidoc += length
1746 else:
1747 asciidoc += self.makeParameterName(length)
1748 asciidoc += ' '
1749
1750 # Void pointers don't actually point at anything - remove the word "to"
1751 if paramtype.text == 'void':
1752 if lengths[-1] == '1':
1753 if len(lengths) > 1:
1754 asciidoc = asciidoc[:-5] # Take care of the extra s added by the post array chunk function. #HACK#
1755 else:
1756 asciidoc = asciidoc[:-4]
1757 else:
1758 # An array of void values is a byte array.
1759 asciidoc += 'byte'
1760
1761 elif paramtype.text == 'char':
1762 # A null terminated array of chars is a string
1763 if lengths[-1] == 'null-terminated':
1764 asciidoc += 'string'
1765 else:
1766 # Else it's just a bunch of chars
1767 asciidoc += 'char value'
1768 elif param.text is not None:
1769 # If a value is "const" that means it won't get modified, so it must be valid going into the function.
1770 if 'const' in param.text:
1771 typecategory = self.getTypeCategory(paramtype.text)
1772 if (typecategory != 'struct' and typecategory != 'union' and typecategory != 'basetype' and typecategory is not None) or not self.isStructAlwaysValid(paramtype.text):
1773 asciidoc += 'valid '
1774
1775 asciidoc += typetext
1776
1777 # pluralize
1778 if len(lengths) > 1 or (lengths[0] != '1' and lengths[0] != 'null-terminated'):
1779 asciidoc += 's'
1780
1781 elif self.paramIsPointer(param):
1782 # Handle pointers - which are really special case arrays (i.e. they don't have a length)
1783 pointercount = paramtype.tail.count('*')
1784
1785 # Could be multi-level pointers (e.g. ppData - pointer to a pointer). Handle that.
1786 for i in range(0, pointercount):
1787 asciidoc += 'a pointer to '
1788
1789 if paramtype.text == 'void':
1790 # If there's only one pointer, it's optional, and it doesn't point at anything in particular - we don't need any language.
1791 if pointercount == 1 and param.attrib.get('optional') is not None:
1792 return '' # early return
1793 else:
1794 # Pointer to nothing in particular - delete the " to " portion
1795 asciidoc = asciidoc[:-4]
1796 else:
1797 # Add an article for English semantic win
1798 asciidoc += 'a '
1799
1800 # If a value is "const" that means it won't get modified, so it must be valid going into the function.
1801 if param.text is not None and paramtype.text != 'void':
1802 if 'const' in param.text:
1803 asciidoc += 'valid '
1804
1805 asciidoc += typetext
1806
1807 else:
1808 # Non-pointer, non-optional things must be valid
1809 asciidoc += 'a valid '
1810 asciidoc += typetext
1811
1812 if asciidoc != '':
1813 asciidoc += '\n'
1814
1815 # Add additional line for non-optional bitmasks
1816 if self.getTypeCategory(paramtype.text) == 'bitmask':
1817 if param.attrib.get('optional') is None:
1818 asciidoc += '* '
1819 if self.paramIsArray(param):
1820 asciidoc += 'Each element of '
1821 asciidoc += 'pname:'
1822 asciidoc += paramname.text
1823 asciidoc += ' mustnot: be `0`'
1824 asciidoc += '\n'
1825
1826 return asciidoc
1827
1828 def makeAsciiDocLineForParameter(self, param, params, typetext):
1829 if param.attrib.get('noautovalidity') is not None:
1830 return ''
1831 asciidoc = self.createValidationLineForParameterIntroChunk(param, params, typetext)
1832
1833 return asciidoc
1834
1835 # Try to do check if a structure is always considered valid (i.e. there's no rules to its acceptance)
1836 def isStructAlwaysValid(self, structname):
1837
1838 struct = self.registry.find("types/type[@name='" + structname + "']")
1839
1840 params = struct.findall('member')
1841 validity = struct.find('validity')
1842
1843 if validity is not None:
1844 return False
1845
1846 for param in params:
1847 paramname = param.find('name')
1848 paramtype = param.find('type')
1849 typecategory = self.getTypeCategory(paramtype.text)
1850
1851 if paramname.text == 'pNext':
1852 return False
1853
1854 if paramname.text == 'sType':
1855 return False
1856
1857 if paramtype.text == 'void' or paramtype.text == 'char' or self.paramIsArray(param) or self.paramIsPointer(param):
1858 if self.makeAsciiDocLineForParameter(param, params, '') != '':
1859 return False
1860 elif typecategory == 'handle' or typecategory == 'enum' or typecategory == 'bitmask' or param.attrib.get('returnedonly') == 'true':
1861 return False
1862 elif typecategory == 'struct' or typecategory == 'union':
1863 if self.isStructAlwaysValid(paramtype.text) is False:
1864 return False
1865
1866 return True
1867
1868 #
1869 # Make an entire asciidoc line for a given parameter
1870 def createValidationLineForParameter(self, param, params, typecategory):
1871 asciidoc = ''
1872 paramname = param.find('name')
1873 paramtype = param.find('type')
1874
1875 if paramtype.text == 'void' or paramtype.text == 'char':
1876 # Chars and void are special cases - needs care inside the generator functions
1877 # A null-terminated char array is a string, else it's chars.
1878 # An array of void values is a byte array, a void pointer is just a pointer to nothing in particular
1879 asciidoc += self.makeAsciiDocLineForParameter(param, params, '')
1880 elif typecategory == 'bitmask':
1881 bitsname = paramtype.text.replace('Flags', 'FlagBits')
1882 if self.registry.find("enums[@name='" + bitsname + "']") is None:
1883 asciidoc += '* '
1884 asciidoc += self.makeParameterName(paramname.text)
1885 asciidoc += ' must: be `0`'
1886 asciidoc += '\n'
1887 else:
1888 if self.paramIsArray(param):
1889 asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combinations of ' + self.makeEnumerationName(bitsname) + ' value')
1890 else:
1891 asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combination of ' + self.makeEnumerationName(bitsname) + ' values')
1892 elif typecategory == 'handle':
1893 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' handle')
1894 elif typecategory == 'enum':
1895 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeEnumerationName(paramtype.text) + ' value')
1896 elif typecategory == 'struct':
1897 if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text):
1898 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' structure')
1899 elif typecategory == 'union':
1900 if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text):
1901 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' union')
1902 elif self.paramIsArray(param) or self.paramIsPointer(param):
1903 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeBaseTypeName(paramtype.text) + ' value')
1904
1905 return asciidoc
1906
1907 #
1908 # Make an asciidoc validity entry for a handle's parent object
1909 def makeAsciiDocHandleParent(self, param, params):
1910 asciidoc = ''
1911 paramname = param.find('name')
1912 paramtype = param.find('type')
1913
1914 # Deal with handle parents
1915 handleparent = self.getHandleParent(paramtype.text)
1916 if handleparent is not None:
1917 parentreference = None
1918 for otherparam in params:
1919 if otherparam.find('type').text == handleparent:
1920 parentreference = otherparam.find('name').text
1921 if parentreference is not None:
1922 asciidoc += '* '
1923
1924 if self.isHandleOptional(param, params):
1925 if self.paramIsArray(param):
1926 asciidoc += 'Each element of '
1927 asciidoc += self.makeParameterName(paramname.text)
1928 asciidoc += ' that is a valid handle'
1929 else:
1930 asciidoc += 'If '
1931 asciidoc += self.makeParameterName(paramname.text)
1932 asciidoc += ' is a valid handle, it'
1933 else:
1934 if self.paramIsArray(param):
1935 asciidoc += 'Each element of '
1936 asciidoc += self.makeParameterName(paramname.text)
1937 asciidoc += ' must: have been created, allocated or retrieved from '
1938 asciidoc += self.makeParameterName(parentreference)
1939
1940 asciidoc += '\n'
1941 return asciidoc
1942
1943 #
1944 # Generate an asciidoc validity line for the sType value of a struct
1945 def makeStructureType(self, blockname, param):
1946 asciidoc = '* '
1947 paramname = param.find('name')
1948 paramtype = param.find('type')
1949
1950 asciidoc += self.makeParameterName(paramname.text)
1951 asciidoc += ' must: be '
1952
1953 structuretype = ''
1954 for elem in re.findall(r'(([A-Z][a-z]+)|([A-Z][A-Z]+))', blockname):
1955 if elem[0] == 'Vk':
1956 structuretype += 'VK_STRUCTURE_TYPE_'
1957 else:
1958 structuretype += elem[0].upper()
1959 structuretype += '_'
1960
1961 asciidoc += self.makeEnumerantName(structuretype[:-1])
1962 asciidoc += '\n'
1963
1964 return asciidoc
1965
1966 #
1967 # Generate an asciidoc validity line for the pNext value of a struct
1968 def makeStructureExtensionPointer(self, param):
1969 asciidoc = '* '
1970 paramname = param.find('name')
1971 paramtype = param.find('type')
1972
1973 asciidoc += self.makeParameterName(paramname.text)
1974
1975 validextensionstructs = param.attrib.get('validextensionstructs')
Dustin Graves94f19142016-05-10 16:44:16 -06001976 asciidoc += ' must: be `NULL`'
1977 if validextensionstructs is not None:
1978 extensionstructs = ['slink:' + x for x in validextensionstructs.split(',')]
1979 asciidoc += ', or a pointer to a valid instance of '
1980 if len(extensionstructs) == 1:
1981 asciidoc += validextensionstructs
1982 else:
1983 asciidoc += (', ').join(extensionstructs[:-1]) + ' or ' + extensionstructs[-1]
Mike Stroyandee76ef2016-01-07 15:35:37 -07001984
1985 asciidoc += '\n'
1986
1987 return asciidoc
1988
1989 #
1990 # Generate all the valid usage information for a given struct or command
1991 def makeValidUsageStatements(self, cmd, blockname, params, usages):
1992 # Start the asciidoc block for this
1993 asciidoc = ''
1994
1995 handles = []
1996 anyparentedhandlesoptional = False
1997 parentdictionary = {}
1998 arraylengths = set()
1999 for param in params:
2000 paramname = param.find('name')
2001 paramtype = param.find('type')
2002
2003 # Get the type's category
2004 typecategory = self.getTypeCategory(paramtype.text)
2005
2006 # Generate language to independently validate a parameter
2007 if paramtype.text == 'VkStructureType' and paramname.text == 'sType':
2008 asciidoc += self.makeStructureType(blockname, param)
2009 elif paramtype.text == 'void' and paramname.text == 'pNext':
2010 asciidoc += self.makeStructureExtensionPointer(param)
2011 else:
2012 asciidoc += self.createValidationLineForParameter(param, params, typecategory)
2013
2014 # Ensure that any parenting is properly validated, and list that a handle was found
2015 if typecategory == 'handle':
2016 # Don't detect a parent for return values!
2017 if not self.paramIsPointer(param) or (param.text is not None and 'const' in param.text):
2018 parent = self.getHandleParent(paramtype.text)
2019 if parent is not None:
2020 handles.append(param)
2021
2022 # If any param is optional, it affects the output
2023 if self.isHandleOptional(param, params):
2024 anyparentedhandlesoptional = True
2025
2026 # Find the first dispatchable parent
2027 ancestor = parent
2028 while ancestor is not None and not self.isHandleTypeDispatchable(ancestor):
2029 ancestor = self.getHandleParent(ancestor)
2030
2031 # If one was found, add this parameter to the parent dictionary
2032 if ancestor is not None:
2033 if ancestor not in parentdictionary:
2034 parentdictionary[ancestor] = []
2035
2036 if self.paramIsArray(param):
2037 parentdictionary[ancestor].append('the elements of ' + self.makeParameterName(paramname.text))
2038 else:
2039 parentdictionary[ancestor].append(self.makeParameterName(paramname.text))
2040
2041 # Get the array length for this parameter
2042 arraylength = param.attrib.get('len')
2043 if arraylength is not None:
2044 for onelength in arraylength.split(','):
2045 arraylengths.add(onelength)
2046
2047 # For any vkQueue* functions, there might be queue type data
2048 if 'vkQueue' in blockname:
2049 # The queue type must be valid
2050 queuetypes = cmd.attrib.get('queues')
2051 if queuetypes is not None:
2052 queuebits = []
2053 for queuetype in re.findall(r'([^,]+)', queuetypes):
2054 queuebits.append(queuetype.replace('_',' '))
2055
2056 asciidoc += '* '
2057 asciidoc += 'The pname:queue must: support '
2058 if len(queuebits) == 1:
2059 asciidoc += queuebits[0]
2060 else:
2061 asciidoc += (', ').join(queuebits[:-1])
2062 asciidoc += ' or '
2063 asciidoc += queuebits[-1]
2064 asciidoc += ' operations'
2065 asciidoc += '\n'
2066
2067 if 'vkCmd' in blockname:
2068 # The commandBuffer parameter must be being recorded
2069 asciidoc += '* '
2070 asciidoc += 'pname:commandBuffer must: be in the recording state'
2071 asciidoc += '\n'
2072
2073 # The queue type must be valid
2074 queuetypes = cmd.attrib.get('queues')
2075 queuebits = []
2076 for queuetype in re.findall(r'([^,]+)', queuetypes):
2077 queuebits.append(queuetype.replace('_',' '))
2078
2079 asciidoc += '* '
2080 asciidoc += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support '
2081 if len(queuebits) == 1:
2082 asciidoc += queuebits[0]
2083 else:
2084 asciidoc += (', ').join(queuebits[:-1])
2085 asciidoc += ' or '
2086 asciidoc += queuebits[-1]
2087 asciidoc += ' operations'
2088 asciidoc += '\n'
2089
2090 # Must be called inside/outside a renderpass appropriately
2091 renderpass = cmd.attrib.get('renderpass')
2092
2093 if renderpass != 'both':
2094 asciidoc += '* This command must: only be called '
2095 asciidoc += renderpass
2096 asciidoc += ' of a render pass instance'
2097 asciidoc += '\n'
2098
2099 # Must be in the right level command buffer
2100 cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
2101
2102 if cmdbufferlevel != 'primary,secondary':
2103 asciidoc += '* pname:commandBuffer must: be a '
2104 asciidoc += cmdbufferlevel
2105 asciidoc += ' sname:VkCommandBuffer'
2106 asciidoc += '\n'
2107
2108 # Any non-optional arraylengths should specify they must be greater than 0
2109 for param in params:
2110 paramname = param.find('name')
2111
2112 for arraylength in arraylengths:
2113 if paramname.text == arraylength and param.attrib.get('optional') is None:
2114 # Get all the array dependencies
2115 arrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
2116
2117 # Get all the optional array dependencies, including those not generating validity for some reason
2118 optionalarrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
2119 optionalarrays.extend(cmd.findall("param/[@len='" + arraylength + "'][@noautovalidity='true']"))
2120
2121 asciidoc += '* '
2122
2123 # Allow lengths to be arbitrary if all their dependents are optional
2124 if len(optionalarrays) == len(arrays) and len(optionalarrays) != 0:
2125 asciidoc += 'If '
2126 if len(optionalarrays) > 1:
2127 asciidoc += 'any of '
2128
2129 for array in optionalarrays[:-1]:
2130 asciidoc += self.makeParameterName(optionalarrays.find('name').text)
2131 asciidoc += ', '
2132
2133 if len(optionalarrays) > 1:
2134 asciidoc += 'and '
2135 asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
2136 asciidoc += ' are '
2137 else:
2138 asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
2139 asciidoc += ' is '
2140
2141 asciidoc += 'not `NULL`, '
2142
2143 if self.paramIsPointer(param):
2144 asciidoc += 'the value referenced by '
Mike Stroyandee76ef2016-01-07 15:35:37 -07002145
2146 elif self.paramIsPointer(param):
2147 asciidoc += 'The value referenced by '
Mike Stroyandee76ef2016-01-07 15:35:37 -07002148
2149 asciidoc += self.makeParameterName(arraylength)
2150 asciidoc += ' must: be greater than `0`'
2151 asciidoc += '\n'
2152
2153 # Find the parents of all objects referenced in this command
2154 for param in handles:
2155 asciidoc += self.makeAsciiDocHandleParent(param, params)
2156
2157 # Find the common ancestors of objects
2158 noancestorscount = 0
2159 while noancestorscount < len(parentdictionary):
2160 noancestorscount = 0
2161 oldparentdictionary = parentdictionary.copy()
2162 for parent in oldparentdictionary.items():
2163 ancestor = self.getHandleParent(parent[0])
2164
2165 while ancestor is not None and ancestor not in parentdictionary:
2166 ancestor = self.getHandleParent(ancestor)
2167
2168 if ancestor is not None:
2169 parentdictionary[ancestor] += parentdictionary.pop(parent[0])
2170 else:
2171 # No ancestors possible - so count it up
2172 noancestorscount += 1
2173
2174 # Add validation language about common ancestors
2175 for parent in parentdictionary.items():
2176 if len(parent[1]) > 1:
2177 parentlanguage = '* '
2178
2179 parentlanguage += 'Each of '
2180 parentlanguage += ", ".join(parent[1][:-1])
2181 parentlanguage += ' and '
2182 parentlanguage += parent[1][-1]
2183 if anyparentedhandlesoptional is True:
2184 parentlanguage += ' that are valid handles'
2185 parentlanguage += ' must: have been created, allocated or retrieved from the same '
2186 parentlanguage += self.makeStructName(parent[0])
2187 parentlanguage += '\n'
2188
2189 # Capitalize and add to the main language
2190 asciidoc += parentlanguage
2191
Dustin Graves3ff520c2016-03-28 16:17:38 -06002192 # Add in any plain-text validation language that should be added
Mike Stroyandee76ef2016-01-07 15:35:37 -07002193 for usage in usages:
2194 asciidoc += '* '
Dustin Graves3ff520c2016-03-28 16:17:38 -06002195 asciidoc += usage
Mike Stroyandee76ef2016-01-07 15:35:37 -07002196 asciidoc += '\n'
2197
2198 # In case there's nothing to report, return None
2199 if asciidoc == '':
2200 return None
2201 # Delimit the asciidoc block
2202 return asciidoc
2203
2204 def makeThreadSafetyBlock(self, cmd, paramtext):
2205 """Generate C function pointer typedef for <command> Element"""
2206 paramdecl = ''
2207
2208 # For any vkCmd* functions, the commandBuffer parameter must be being recorded
2209 if cmd.find('proto/name') is not None and 'vkCmd' in cmd.find('proto/name'):
2210 paramdecl += '* '
2211 paramdecl += 'The sname:VkCommandPool that pname:commandBuffer was created from'
2212 paramdecl += '\n'
2213
2214 # Find and add any parameters that are thread unsafe
2215 explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
2216 if (explicitexternsyncparams is not None):
2217 for param in explicitexternsyncparams:
2218 externsyncattribs = param.attrib.get('externsync')
2219 paramname = param.find('name')
2220 for externsyncattrib in externsyncattribs.split(','):
2221 paramdecl += '* '
2222 paramdecl += 'Host access to '
2223 if externsyncattrib == 'true':
2224 if self.paramIsArray(param):
2225 paramdecl += 'each member of ' + self.makeParameterName(paramname.text)
2226 elif self.paramIsPointer(param):
2227 paramdecl += 'the object referenced by ' + self.makeParameterName(paramname.text)
2228 else:
2229 paramdecl += self.makeParameterName(paramname.text)
2230 else:
2231 paramdecl += 'pname:'
2232 paramdecl += externsyncattrib
2233 paramdecl += ' must: be externally synchronized\n'
2234
2235 # Find and add any "implicit" parameters that are thread unsafe
2236 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2237 if (implicitexternsyncparams is not None):
2238 for elem in implicitexternsyncparams:
2239 paramdecl += '* '
2240 paramdecl += 'Host access to '
2241 paramdecl += elem.text
2242 paramdecl += ' must: be externally synchronized\n'
2243
2244 if (paramdecl == ''):
2245 return None
2246 else:
2247 return paramdecl
2248
2249 def makeCommandPropertiesTableEntry(self, cmd, name):
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002250
Mike Stroyandee76ef2016-01-07 15:35:37 -07002251 if 'vkCmd' in name:
2252 # Must be called inside/outside a renderpass appropriately
2253 cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
2254 cmdbufferlevel = (' + \n').join(cmdbufferlevel.title().split(','))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002255
Mike Stroyandee76ef2016-01-07 15:35:37 -07002256 renderpass = cmd.attrib.get('renderpass')
2257 renderpass = renderpass.capitalize()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002258
Mike Stroyandee76ef2016-01-07 15:35:37 -07002259 queues = cmd.attrib.get('queues')
2260 queues = (' + \n').join(queues.upper().split(','))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002261
2262 return '|' + cmdbufferlevel + '|' + renderpass + '|' + queues
Mike Stroyandee76ef2016-01-07 15:35:37 -07002263 elif 'vkQueue' in name:
2264 # Must be called inside/outside a renderpass appropriately
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002265
Mike Stroyandee76ef2016-01-07 15:35:37 -07002266 queues = cmd.attrib.get('queues')
2267 if queues is None:
2268 queues = 'Any'
2269 else:
2270 queues = (' + \n').join(queues.upper().split(','))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002271
2272 return '|-|-|' + queues
Mike Stroyandee76ef2016-01-07 15:35:37 -07002273
2274 return None
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002275
Mike Stroyandee76ef2016-01-07 15:35:37 -07002276 def makeSuccessCodes(self, cmd, name):
2277
2278 successcodes = cmd.attrib.get('successcodes')
2279 if successcodes is not None:
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002280
Mike Stroyandee76ef2016-01-07 15:35:37 -07002281 successcodeentry = ''
2282 successcodes = successcodes.split(',')
Dustin Graves3ff520c2016-03-28 16:17:38 -06002283 return '* ename:' + '\n* ename:'.join(successcodes)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002284
2285 return None
2286
2287 def makeErrorCodes(self, cmd, name):
2288
2289 errorcodes = cmd.attrib.get('errorcodes')
2290 if errorcodes is not None:
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002291
Mike Stroyandee76ef2016-01-07 15:35:37 -07002292 errorcodeentry = ''
2293 errorcodes = errorcodes.split(',')
Dustin Graves3ff520c2016-03-28 16:17:38 -06002294 return '* ename:' + '\n* ename:'.join(errorcodes)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002295
2296 return None
2297
2298 #
2299 # Command generation
2300 def genCmd(self, cmdinfo, name):
2301 OutputGenerator.genCmd(self, cmdinfo, name)
2302 #
Dustin Graves3ff520c2016-03-28 16:17:38 -06002303 # Get all the parameters
Mike Stroyandee76ef2016-01-07 15:35:37 -07002304 params = cmdinfo.elem.findall('param')
Dustin Graves3ff520c2016-03-28 16:17:38 -06002305 usageelements = cmdinfo.elem.findall('validity/usage')
2306 usages = []
2307
2308 for usage in usageelements:
2309 usages.append(usage.text)
2310 for usage in cmdinfo.additionalValidity:
2311 usages.append(usage.text)
2312 for usage in cmdinfo.removedValidity:
2313 usages.remove(usage.text)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002314
2315 validity = self.makeValidUsageStatements(cmdinfo.elem, name, params, usages)
2316 threadsafety = self.makeThreadSafetyBlock(cmdinfo.elem, 'param')
2317 commandpropertiesentry = self.makeCommandPropertiesTableEntry(cmdinfo.elem, name)
2318 successcodes = self.makeSuccessCodes(cmdinfo.elem, name)
2319 errorcodes = self.makeErrorCodes(cmdinfo.elem, name)
2320
2321 self.writeInclude('validity/protos', name, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes)
2322
2323 #
2324 # Struct Generation
2325 def genStruct(self, typeinfo, typename):
2326 OutputGenerator.genStruct(self, typeinfo, typename)
2327
2328 # Anything that's only ever returned can't be set by the user, so shouldn't have any validity information.
2329 if typeinfo.elem.attrib.get('returnedonly') is None:
2330 params = typeinfo.elem.findall('member')
Dustin Graves3ff520c2016-03-28 16:17:38 -06002331
2332 usageelements = typeinfo.elem.findall('validity/usage')
2333 usages = []
2334
2335 for usage in usageelements:
2336 usages.append(usage.text)
2337 for usage in typeinfo.additionalValidity:
2338 usages.append(usage.text)
2339 for usage in typeinfo.removedValidity:
2340 usages.remove(usage.text)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002341
2342 validity = self.makeValidUsageStatements(typeinfo.elem, typename, params, usages)
2343 threadsafety = self.makeThreadSafetyBlock(typeinfo.elem, 'member')
2344
2345 self.writeInclude('validity/structs', typename, validity, threadsafety, None, None, None)
2346 else:
2347 # Still generate files for return only structs, in case this state changes later
2348 self.writeInclude('validity/structs', typename, None, None, None, None, None)
2349
2350 #
2351 # Type Generation
2352 def genType(self, typeinfo, typename):
2353 OutputGenerator.genType(self, typeinfo, typename)
2354
2355 category = typeinfo.elem.get('category')
2356 if (category == 'struct' or category == 'union'):
2357 self.genStruct(typeinfo, typename)
2358
2359# HostSynchronizationOutputGenerator - subclass of OutputGenerator.
2360# Generates AsciiDoc includes of the externsync parameter table for the
2361# fundamentals chapter of the Vulkan specification. Similar to
2362# DocOutputGenerator.
2363#
2364# ---- methods ----
2365# HostSynchronizationOutputGenerator(errFile, warnFile, diagFile) - args as for
2366# OutputGenerator. Defines additional internal state.
2367# ---- methods overriding base class ----
2368# genCmd(cmdinfo)
2369class HostSynchronizationOutputGenerator(OutputGenerator):
2370 # Generate Host Synchronized Parameters in a table at the top of the spec
2371 def __init__(self,
2372 errFile = sys.stderr,
2373 warnFile = sys.stderr,
2374 diagFile = sys.stdout):
2375 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2376
2377 threadsafety = {'parameters': '', 'parameterlists': '', 'implicit': ''}
2378
2379 def makeParameterName(self, name):
2380 return 'pname:' + name
2381
2382 def makeFLink(self, name):
2383 return 'flink:' + name
2384
2385 #
2386 # Generate an include file
2387 #
2388 # directory - subdirectory to put file in
2389 # basename - base name of the file
2390 # contents - contents of the file (Asciidoc boilerplate aside)
2391 def writeInclude(self):
2392
2393 if self.threadsafety['parameters'] is not None:
2394 # Create file
2395 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameters.txt'
2396 self.logMsg('diag', '# Generating include file:', filename)
2397 fp = open(filename, 'w')
2398
2399 # Host Synchronization
Dustin Graves3ff520c2016-03-28 16:17:38 -06002400 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002401 write('.Externally Synchronized Parameters', file=fp)
2402 write('*' * 80, file=fp)
2403 write(self.threadsafety['parameters'], file=fp, end='')
2404 write('*' * 80, file=fp)
2405 write('', file=fp)
2406
2407 if self.threadsafety['parameterlists'] is not None:
2408 # Create file
2409 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameterlists.txt'
2410 self.logMsg('diag', '# Generating include file:', filename)
2411 fp = open(filename, 'w')
2412
2413 # Host Synchronization
Dustin Graves3ff520c2016-03-28 16:17:38 -06002414 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002415 write('.Externally Synchronized Parameter Lists', file=fp)
2416 write('*' * 80, file=fp)
2417 write(self.threadsafety['parameterlists'], file=fp, end='')
2418 write('*' * 80, file=fp)
2419 write('', file=fp)
2420
2421 if self.threadsafety['implicit'] is not None:
2422 # Create file
2423 filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/implicit.txt'
2424 self.logMsg('diag', '# Generating include file:', filename)
2425 fp = open(filename, 'w')
2426
2427 # Host Synchronization
Dustin Graves3ff520c2016-03-28 16:17:38 -06002428 write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
Mike Stroyandee76ef2016-01-07 15:35:37 -07002429 write('.Implicit Externally Synchronized Parameters', file=fp)
2430 write('*' * 80, file=fp)
2431 write(self.threadsafety['implicit'], file=fp, end='')
2432 write('*' * 80, file=fp)
2433 write('', file=fp)
2434
2435 fp.close()
2436
2437 #
2438 # Check if the parameter passed in is a pointer to an array
2439 def paramIsArray(self, param):
2440 return param.attrib.get('len') is not None
2441
2442 # Check if the parameter passed in is a pointer
2443 def paramIsPointer(self, param):
2444 ispointer = False
2445 paramtype = param.find('type')
2446 if paramtype.tail is not None and '*' in paramtype.tail:
2447 ispointer = True
2448
2449 return ispointer
2450
2451 # Turn the "name[].member[]" notation into plain English.
2452 def makeThreadDereferenceHumanReadable(self, dereference):
2453 matches = re.findall(r"[\w]+[^\w]*",dereference)
2454 stringval = ''
2455 for match in reversed(matches):
2456 if '->' in match or '.' in match:
2457 stringval += 'member of '
2458 if '[]' in match:
2459 stringval += 'each element of '
2460
2461 stringval += 'the '
2462 stringval += self.makeParameterName(re.findall(r"[\w]+",match)[0])
2463 stringval += ' '
2464
2465 stringval += 'parameter'
2466
2467 return stringval[0].upper() + stringval[1:]
2468
2469 def makeThreadSafetyBlocks(self, cmd, paramtext):
2470 protoname = cmd.find('proto/name').text
2471
2472 # Find and add any parameters that are thread unsafe
2473 explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
2474 if (explicitexternsyncparams is not None):
2475 for param in explicitexternsyncparams:
2476 externsyncattribs = param.attrib.get('externsync')
2477 paramname = param.find('name')
2478 for externsyncattrib in externsyncattribs.split(','):
2479
2480 tempstring = '* '
2481 if externsyncattrib == 'true':
2482 if self.paramIsArray(param):
2483 tempstring += 'Each element of the '
2484 elif self.paramIsPointer(param):
2485 tempstring += 'The object referenced by the '
2486 else:
2487 tempstring += 'The '
2488
2489 tempstring += self.makeParameterName(paramname.text)
2490 tempstring += ' parameter'
2491
2492 else:
2493 tempstring += self.makeThreadDereferenceHumanReadable(externsyncattrib)
2494
2495 tempstring += ' in '
2496 tempstring += self.makeFLink(protoname)
2497 tempstring += '\n'
2498
2499
2500 if ' element of ' in tempstring:
2501 self.threadsafety['parameterlists'] += tempstring
2502 else:
2503 self.threadsafety['parameters'] += tempstring
2504
2505
2506 # Find and add any "implicit" parameters that are thread unsafe
2507 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2508 if (implicitexternsyncparams is not None):
2509 for elem in implicitexternsyncparams:
2510 self.threadsafety['implicit'] += '* '
2511 self.threadsafety['implicit'] += elem.text[0].upper()
2512 self.threadsafety['implicit'] += elem.text[1:]
2513 self.threadsafety['implicit'] += ' in '
2514 self.threadsafety['implicit'] += self.makeFLink(protoname)
2515 self.threadsafety['implicit'] += '\n'
2516
2517
2518 # For any vkCmd* functions, the commandBuffer parameter must be being recorded
2519 if protoname is not None and 'vkCmd' in protoname:
2520 self.threadsafety['implicit'] += '* '
2521 self.threadsafety['implicit'] += 'The sname:VkCommandPool that pname:commandBuffer was allocated from, in '
2522 self.threadsafety['implicit'] += self.makeFLink(protoname)
2523
2524 self.threadsafety['implicit'] += '\n'
2525
2526 #
2527 # Command generation
2528 def genCmd(self, cmdinfo, name):
2529 OutputGenerator.genCmd(self, cmdinfo, name)
2530 #
2531 # Get all thh parameters
2532 params = cmdinfo.elem.findall('param')
2533 usages = cmdinfo.elem.findall('validity/usage')
2534
2535 self.makeThreadSafetyBlocks(cmdinfo.elem, 'param')
2536
2537 self.writeInclude()
Mike Stroyan845bdc42015-11-02 15:30:20 -07002538
2539# ThreadOutputGenerator - subclass of OutputGenerator.
2540# Generates Thread checking framework
2541#
2542# ---- methods ----
2543# ThreadOutputGenerator(errFile, warnFile, diagFile) - args as for
2544# OutputGenerator. Defines additional internal state.
2545# ---- methods overriding base class ----
2546# beginFile(genOpts)
2547# endFile()
2548# beginFeature(interface, emit)
2549# endFeature()
2550# genType(typeinfo,name)
2551# genStruct(typeinfo,name)
2552# genGroup(groupinfo,name)
2553# genEnum(enuminfo, name)
2554# genCmd(cmdinfo)
2555class ThreadOutputGenerator(OutputGenerator):
2556 """Generate specified API interfaces in a specific style, such as a C header"""
2557 # This is an ordered list of sections in the header file.
2558 TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
2559 'group', 'bitmask', 'funcpointer', 'struct']
2560 ALL_SECTIONS = TYPE_SECTIONS + ['command']
2561 def __init__(self,
2562 errFile = sys.stderr,
2563 warnFile = sys.stderr,
2564 diagFile = sys.stdout):
2565 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2566 # Internal state - accumulators for different inner block text
2567 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2568 self.intercepts = []
2569
2570 # Check if the parameter passed in is a pointer to an array
2571 def paramIsArray(self, param):
2572 return param.attrib.get('len') is not None
2573
2574 # Check if the parameter passed in is a pointer
2575 def paramIsPointer(self, param):
2576 ispointer = False
2577 for elem in param:
2578 #write('paramIsPointer '+elem.text, file=sys.stderr)
2579 #write('elem.tag '+elem.tag, file=sys.stderr)
2580 #if (elem.tail is None):
2581 # write('elem.tail is None', file=sys.stderr)
2582 #else:
2583 # write('elem.tail '+elem.tail, file=sys.stderr)
2584 if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
2585 ispointer = True
2586 # write('is pointer', file=sys.stderr)
2587 return ispointer
2588 def makeThreadUseBlock(self, cmd, functionprefix):
2589 """Generate C function pointer typedef for <command> Element"""
2590 paramdecl = ''
2591 thread_check_dispatchable_objects = [
2592 "VkCommandBuffer",
2593 "VkDevice",
2594 "VkInstance",
2595 "VkQueue",
2596 ]
2597 thread_check_nondispatchable_objects = [
2598 "VkBuffer",
2599 "VkBufferView",
2600 "VkCommandPool",
2601 "VkDescriptorPool",
2602 "VkDescriptorSetLayout",
2603 "VkDeviceMemory",
2604 "VkEvent",
2605 "VkFence",
2606 "VkFramebuffer",
2607 "VkImage",
2608 "VkImageView",
2609 "VkPipeline",
2610 "VkPipelineCache",
2611 "VkPipelineLayout",
2612 "VkQueryPool",
2613 "VkRenderPass",
2614 "VkSampler",
2615 "VkSemaphore",
2616 "VkShaderModule",
2617 ]
2618
2619 # Find and add any parameters that are thread unsafe
2620 params = cmd.findall('param')
2621 for param in params:
2622 paramname = param.find('name')
2623 if False: # self.paramIsPointer(param):
2624 paramdecl += ' // not watching use of pointer ' + paramname.text + '\n'
2625 else:
2626 externsync = param.attrib.get('externsync')
2627 if externsync == 'true':
2628 if self.paramIsArray(param):
Michael Mc Donnellc133bd02016-03-17 21:18:32 -07002629 paramdecl += ' for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
Mike Stroyan845bdc42015-11-02 15:30:20 -07002630 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + '[index]);\n'
2631 paramdecl += ' }\n'
2632 else:
2633 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + ');\n'
2634 elif (param.attrib.get('externsync')):
2635 if self.paramIsArray(param):
2636 # Externsync can list pointers to arrays of members to synchronize
Michael Mc Donnellc133bd02016-03-17 21:18:32 -07002637 paramdecl += ' for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
Mike Stroyan845bdc42015-11-02 15:30:20 -07002638 for member in externsync.split(","):
2639 # Replace first empty [] in member name with index
2640 element = member.replace('[]','[index]',1)
2641 if '[]' in element:
2642 # Replace any second empty [] in element name with
2643 # inner array index based on mapping array names like
2644 # "pSomeThings[]" to "someThingCount" array size.
2645 # This could be more robust by mapping a param member
2646 # name to a struct type and "len" attribute.
2647 limit = element[0:element.find('s[]')] + 'Count'
2648 dotp = limit.rfind('.p')
2649 limit = limit[0:dotp+1] + limit[dotp+2:dotp+3].lower() + limit[dotp+3:]
Mike Stroyan0b64aee2016-07-13 10:10:25 -06002650 paramdecl += ' for(uint32_t index2=0;index2<'+limit+';index2++)\n'
Mike Stroyan845bdc42015-11-02 15:30:20 -07002651 element = element.replace('[]','[index2]')
Mike Stroyan0b64aee2016-07-13 10:10:25 -06002652 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + element + ');\n'
Mike Stroyan845bdc42015-11-02 15:30:20 -07002653 paramdecl += ' }\n'
2654 else:
2655 # externsync can list members to synchronize
2656 for member in externsync.split(","):
Mark Lobodzinski3b34af02016-09-27 13:08:15 -06002657 member = str(member).replace("::", "->")
Mike Stroyan845bdc42015-11-02 15:30:20 -07002658 paramdecl += ' ' + functionprefix + 'WriteObject(my_data, ' + member + ');\n'
2659 else:
2660 paramtype = param.find('type')
2661 if paramtype is not None:
2662 paramtype = paramtype.text
2663 else:
2664 paramtype = 'None'
2665 if paramtype in thread_check_dispatchable_objects or paramtype in thread_check_nondispatchable_objects:
2666 if self.paramIsArray(param) and ('pPipelines' != paramname.text):
Michael Mc Donnellc133bd02016-03-17 21:18:32 -07002667 paramdecl += ' for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
Mike Stroyan845bdc42015-11-02 15:30:20 -07002668 paramdecl += ' ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + '[index]);\n'
2669 paramdecl += ' }\n'
2670 elif not self.paramIsPointer(param):
2671 # Pointer params are often being created.
2672 # They are not being read from.
2673 paramdecl += ' ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + ');\n'
2674 explicitexternsyncparams = cmd.findall("param[@externsync]")
2675 if (explicitexternsyncparams is not None):
2676 for param in explicitexternsyncparams:
2677 externsyncattrib = param.attrib.get('externsync')
2678 paramname = param.find('name')
Mike Stroyan0b64aee2016-07-13 10:10:25 -06002679 paramdecl += ' // Host access to '
Mike Stroyan845bdc42015-11-02 15:30:20 -07002680 if externsyncattrib == 'true':
2681 if self.paramIsArray(param):
2682 paramdecl += 'each member of ' + paramname.text
2683 elif self.paramIsPointer(param):
2684 paramdecl += 'the object referenced by ' + paramname.text
2685 else:
2686 paramdecl += paramname.text
2687 else:
2688 paramdecl += externsyncattrib
2689 paramdecl += ' must be externally synchronized\n'
2690
2691 # Find and add any "implicit" parameters that are thread unsafe
2692 implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2693 if (implicitexternsyncparams is not None):
2694 for elem in implicitexternsyncparams:
2695 paramdecl += ' // '
2696 paramdecl += elem.text
2697 paramdecl += ' must be externally synchronized between host accesses\n'
2698
2699 if (paramdecl == ''):
2700 return None
2701 else:
2702 return paramdecl
2703 def beginFile(self, genOpts):
2704 OutputGenerator.beginFile(self, genOpts)
2705 # C-specific
2706 #
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002707 # Multiple inclusion protection & C++ namespace.
Mike Stroyan845bdc42015-11-02 15:30:20 -07002708 if (genOpts.protectFile and self.genOpts.filename):
2709 headerSym = '__' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename))
2710 write('#ifndef', headerSym, file=self.outFile)
2711 write('#define', headerSym, '1', file=self.outFile)
2712 self.newline()
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002713 write('namespace threading {', file=self.outFile)
Mike Stroyan845bdc42015-11-02 15:30:20 -07002714 self.newline()
2715 #
2716 # User-supplied prefix text, if any (list of strings)
2717 if (genOpts.prefixText):
2718 for s in genOpts.prefixText:
2719 write(s, file=self.outFile)
2720 def endFile(self):
2721 # C-specific
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002722 # Finish C++ namespace and multiple inclusion protection
Mike Stroyan845bdc42015-11-02 15:30:20 -07002723 self.newline()
2724 # record intercepted procedures
2725 write('// intercepts', file=self.outFile)
2726 write('struct { const char* name; PFN_vkVoidFunction pFunc;} procmap[] = {', file=self.outFile)
2727 write('\n'.join(self.intercepts), file=self.outFile)
2728 write('};\n', file=self.outFile)
2729 self.newline()
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002730 write('} // namespace threading', file=self.outFile)
Mike Stroyan845bdc42015-11-02 15:30:20 -07002731 if (self.genOpts.protectFile and self.genOpts.filename):
2732 self.newline()
2733 write('#endif', file=self.outFile)
2734 # Finish processing in superclass
2735 OutputGenerator.endFile(self)
2736 def beginFeature(self, interface, emit):
2737 #write('// starting beginFeature', file=self.outFile)
2738 # Start processing in superclass
2739 OutputGenerator.beginFeature(self, interface, emit)
2740 # C-specific
2741 # Accumulate includes, defines, types, enums, function pointer typedefs,
2742 # end function prototypes separately for this feature. They're only
2743 # printed in endFeature().
2744 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2745 #write('// ending beginFeature', file=self.outFile)
2746 def endFeature(self):
2747 # C-specific
2748 # Actually write the interface to the output file.
2749 #write('// starting endFeature', file=self.outFile)
2750 if (self.emit):
2751 self.newline()
2752 if (self.genOpts.protectFeature):
2753 write('#ifndef', self.featureName, file=self.outFile)
2754 # If type declarations are needed by other features based on
2755 # this one, it may be necessary to suppress the ExtraProtect,
2756 # or move it below the 'for section...' loop.
2757 #write('// endFeature looking at self.featureExtraProtect', file=self.outFile)
2758 if (self.featureExtraProtect != None):
2759 write('#ifdef', self.featureExtraProtect, file=self.outFile)
2760 #write('#define', self.featureName, '1', file=self.outFile)
2761 for section in self.TYPE_SECTIONS:
2762 #write('// endFeature writing section'+section, file=self.outFile)
2763 contents = self.sections[section]
2764 if contents:
2765 write('\n'.join(contents), file=self.outFile)
2766 self.newline()
2767 #write('// endFeature looking at self.sections[command]', file=self.outFile)
2768 if (self.sections['command']):
2769 write('\n'.join(self.sections['command']), end='', file=self.outFile)
2770 self.newline()
2771 if (self.featureExtraProtect != None):
2772 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
2773 if (self.genOpts.protectFeature):
2774 write('#endif /*', self.featureName, '*/', file=self.outFile)
2775 # Finish processing in superclass
2776 OutputGenerator.endFeature(self)
2777 #write('// ending endFeature', file=self.outFile)
2778 #
2779 # Append a definition to the specified section
2780 def appendSection(self, section, text):
2781 # self.sections[section].append('SECTION: ' + section + '\n')
2782 self.sections[section].append(text)
2783 #
2784 # Type generation
2785 def genType(self, typeinfo, name):
2786 pass
2787 #
2788 # Struct (e.g. C "struct" type) generation.
2789 # This is a special case of the <type> tag where the contents are
2790 # interpreted as a set of <member> tags instead of freeform C
2791 # C type declarations. The <member> tags are just like <param>
2792 # tags - they are a declaration of a struct or union member.
2793 # Only simple member declarations are supported (no nested
2794 # structs etc.)
2795 def genStruct(self, typeinfo, typeName):
2796 OutputGenerator.genStruct(self, typeinfo, typeName)
2797 body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
2798 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
2799 for member in typeinfo.elem.findall('.//member'):
2800 body += self.makeCParamDecl(member, self.genOpts.alignFuncParam)
2801 body += ';\n'
2802 body += '} ' + typeName + ';\n'
2803 self.appendSection('struct', body)
2804 #
2805 # Group (e.g. C "enum" type) generation.
2806 # These are concatenated together with other types.
2807 def genGroup(self, groupinfo, groupName):
2808 pass
2809 # Enumerant generation
2810 # <enum> tags may specify their values in several ways, but are usually
2811 # just integers.
2812 def genEnum(self, enuminfo, name):
2813 pass
2814 #
2815 # Command generation
2816 def genCmd(self, cmdinfo, name):
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002817 # Commands shadowed by interface functions and are not implemented
2818 interface_functions = [
2819 'vkEnumerateInstanceLayerProperties',
2820 'vkEnumerateInstanceExtensionProperties',
2821 'vkEnumerateDeviceLayerProperties',
2822 ]
2823 if name in interface_functions:
2824 return
Mike Stroyan845bdc42015-11-02 15:30:20 -07002825 special_functions = [
2826 'vkGetDeviceProcAddr',
2827 'vkGetInstanceProcAddr',
2828 'vkCreateDevice',
2829 'vkDestroyDevice',
2830 'vkCreateInstance',
2831 'vkDestroyInstance',
Mike Stroyan845bdc42015-11-02 15:30:20 -07002832 'vkAllocateCommandBuffers',
2833 'vkFreeCommandBuffers',
2834 'vkCreateDebugReportCallbackEXT',
2835 'vkDestroyDebugReportCallbackEXT',
2836 ]
2837 if name in special_functions:
Chia-I Wu6e8f0d92016-05-16 09:58:50 +08002838 decls = self.makeCDecls(cmdinfo.elem)
2839 self.appendSection('command', '')
2840 self.appendSection('command', '// declare only')
2841 self.appendSection('command', decls[0])
Chia-I Wu33b4ce82016-05-16 10:02:06 +08002842 self.intercepts += [ ' {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name[2:]) ]
Mike Stroyan845bdc42015-11-02 15:30:20 -07002843 return
2844 if "KHR" in name:
2845 self.appendSection('command', '// TODO - not wrapping KHR function ' + name)
2846 return
Dustin Graves94f19142016-05-10 16:44:16 -06002847 if ("DebugMarker" in name) and ("EXT" in name):
2848 self.appendSection('command', '// TODO - not wrapping EXT function ' + name)
2849 return
Mike Stroyan845bdc42015-11-02 15:30:20 -07002850 # Determine first if this function needs to be intercepted
2851 startthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'start')
2852 if startthreadsafety is None:
2853 return
2854 finishthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'finish')
2855 # record that the function will be intercepted
2856 if (self.featureExtraProtect != None):
2857 self.intercepts += [ '#ifdef %s' % self.featureExtraProtect ]
Chia-I Wu33b4ce82016-05-16 10:02:06 +08002858 self.intercepts += [ ' {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name[2:]) ]
Mike Stroyan845bdc42015-11-02 15:30:20 -07002859 if (self.featureExtraProtect != None):
2860 self.intercepts += [ '#endif' ]
2861
2862 OutputGenerator.genCmd(self, cmdinfo, name)
2863 #
2864 decls = self.makeCDecls(cmdinfo.elem)
2865 self.appendSection('command', '')
2866 self.appendSection('command', decls[0][:-1])
2867 self.appendSection('command', '{')
2868 # setup common to call wrappers
2869 # first parameter is always dispatchable
2870 dispatchable_type = cmdinfo.elem.find('param/type').text
2871 dispatchable_name = cmdinfo.elem.find('param/name').text
2872 self.appendSection('command', ' dispatch_key key = get_dispatch_key('+dispatchable_name+');')
2873 self.appendSection('command', ' layer_data *my_data = get_my_data_ptr(key, layer_data_map);')
2874 if dispatchable_type in ["VkPhysicalDevice", "VkInstance"]:
2875 self.appendSection('command', ' VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table;')
2876 else:
2877 self.appendSection('command', ' VkLayerDispatchTable *pTable = my_data->device_dispatch_table;')
2878 # Declare result variable, if any.
2879 resulttype = cmdinfo.elem.find('proto/type')
2880 if (resulttype != None and resulttype.text == 'void'):
2881 resulttype = None
2882 if (resulttype != None):
2883 self.appendSection('command', ' ' + resulttype.text + ' result;')
2884 assignresult = 'result = '
2885 else:
2886 assignresult = ''
2887
Mike Stroyan0b64aee2016-07-13 10:10:25 -06002888 self.appendSection('command', ' bool threadChecks = startMultiThread();')
2889 self.appendSection('command', ' if (threadChecks) {')
2890 self.appendSection('command', " "+"\n ".join(str(startthreadsafety).rstrip().split("\n")))
2891 self.appendSection('command', ' }')
Mike Stroyan845bdc42015-11-02 15:30:20 -07002892 params = cmdinfo.elem.findall('param/name')
2893 paramstext = ','.join([str(param.text) for param in params])
2894 API = cmdinfo.elem.attrib.get('name').replace('vk','pTable->',1)
2895 self.appendSection('command', ' ' + assignresult + API + '(' + paramstext + ');')
Mike Stroyan0b64aee2016-07-13 10:10:25 -06002896 self.appendSection('command', ' if (threadChecks) {')
2897 self.appendSection('command', " "+"\n ".join(str(finishthreadsafety).rstrip().split("\n")))
2898 self.appendSection('command', ' } else {')
2899 self.appendSection('command', ' finishMultiThread();')
2900 self.appendSection('command', ' }')
Mike Stroyan845bdc42015-11-02 15:30:20 -07002901 # Return result variable, if any.
2902 if (resulttype != None):
2903 self.appendSection('command', ' return result;')
2904 self.appendSection('command', '}')
Chia-I Wu33b4ce82016-05-16 10:02:06 +08002905 #
2906 # override makeProtoName to drop the "vk" prefix
2907 def makeProtoName(self, name, tail):
2908 return self.genOpts.apientry + name[2:] + tail
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002909
2910# ParamCheckerOutputGenerator - subclass of OutputGenerator.
2911# Generates param checker layer code.
2912#
2913# ---- methods ----
2914# ParamCheckerOutputGenerator(errFile, warnFile, diagFile) - args as for
2915# OutputGenerator. Defines additional internal state.
2916# ---- methods overriding base class ----
2917# beginFile(genOpts)
2918# endFile()
2919# beginFeature(interface, emit)
2920# endFeature()
2921# genType(typeinfo,name)
2922# genStruct(typeinfo,name)
2923# genGroup(groupinfo,name)
2924# genEnum(enuminfo, name)
2925# genCmd(cmdinfo)
2926class ParamCheckerOutputGenerator(OutputGenerator):
2927 """Generate ParamChecker code based on XML element attributes"""
2928 # This is an ordered list of sections in the header file.
2929 ALL_SECTIONS = ['command']
2930 def __init__(self,
2931 errFile = sys.stderr,
2932 warnFile = sys.stderr,
2933 diagFile = sys.stdout):
2934 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2935 self.INDENT_SPACES = 4
2936 # Commands to ignore
Dustin Graveseec48bf2016-03-02 18:23:29 -07002937 self.blacklist = [
Dustin Graves842621d2016-03-03 14:17:08 -07002938 'vkGetInstanceProcAddr',
2939 'vkGetDeviceProcAddr',
Dustin Graveseec48bf2016-03-02 18:23:29 -07002940 'vkEnumerateInstanceLayerProperties',
2941 'vkEnumerateInstanceExtensionsProperties',
2942 'vkEnumerateDeviceLayerProperties',
2943 'vkEnumerateDeviceExtensionsProperties',
2944 'vkCreateDebugReportCallbackEXT',
2945 'vkDebugReportMessageEXT']
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06002946 # Validation conditions for some special case struct members that are conditionally validated
2947 self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
Dustin Graves6e4c2be2016-07-19 13:17:35 -06002948 # Header version
2949 self.headerVersion = None
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002950 # Internal state - accumulators for different inner block text
2951 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
Dustin Graveseec48bf2016-03-02 18:23:29 -07002952 self.structNames = [] # List of Vulkan struct typenames
2953 self.stypes = [] # Values from the VkStructureType enumeration
2954 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
Dustin Graves20fd66f2016-04-18 18:33:21 -06002955 self.handleTypes = set() # Set of handle type names
Dustin Graveseec48bf2016-03-02 18:23:29 -07002956 self.commands = [] # List of CommandData records for all Vulkan commands
2957 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06002958 self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type
Dustin Graves29148ff2016-03-23 19:44:00 -06002959 self.enumRanges = dict() # Map of enum name to BEGIN/END range values
Dustin Gravesc900f572016-05-16 11:07:59 -06002960 self.flags = set() # Map of flags typenames
Dustin Graves9c6b62b2016-04-26 15:37:10 -06002961 self.flagBits = dict() # Map of flag bits typename to list of values
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002962 # Named tuples to store struct and command data
2963 self.StructType = namedtuple('StructType', ['name', 'value'])
Dustin Graves29148ff2016-03-23 19:44:00 -06002964 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
Dustin Graves20fd66f2016-04-18 18:33:21 -06002965 'isconst', 'isoptional', 'iscount', 'noautovalidity', 'len', 'extstructs',
2966 'condition', 'cdecl'])
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002967 self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl'])
Dustin Graveseec48bf2016-03-02 18:23:29 -07002968 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002969 #
2970 def incIndent(self, indent):
2971 inc = ' ' * self.INDENT_SPACES
2972 if indent:
2973 return indent + inc
2974 return inc
Dustin Graves842621d2016-03-03 14:17:08 -07002975 #
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07002976 def decIndent(self, indent):
2977 if indent and (len(indent) > self.INDENT_SPACES):
2978 return indent[:-self.INDENT_SPACES]
2979 return ''
2980 #
2981 def beginFile(self, genOpts):
2982 OutputGenerator.beginFile(self, genOpts)
2983 # C-specific
2984 #
2985 # User-supplied prefix text, if any (list of strings)
2986 if (genOpts.prefixText):
2987 for s in genOpts.prefixText:
2988 write(s, file=self.outFile)
2989 #
2990 # Multiple inclusion protection & C++ wrappers.
2991 if (genOpts.protectFile and self.genOpts.filename):
2992 headerSym = re.sub('\.h', '_H', os.path.basename(self.genOpts.filename)).upper()
2993 write('#ifndef', headerSym, file=self.outFile)
2994 write('#define', headerSym, '1', file=self.outFile)
2995 self.newline()
2996 #
2997 # Headers
Dustin Gravesb210cee2016-03-31 09:50:42 -06002998 write('#include <string>', file=self.outFile)
2999 self.newline()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003000 write('#include "vulkan/vulkan.h"', file=self.outFile)
Dustin Graves58c2f662016-03-08 17:48:20 -07003001 write('#include "vk_layer_extension_utils.h"', file=self.outFile)
Mark Lobodzinski739391a2016-03-17 15:08:18 -06003002 write('#include "parameter_validation_utils.h"', file=self.outFile)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003003 #
3004 # Macros
3005 self.newline()
3006 write('#ifndef UNUSED_PARAMETER', file=self.outFile)
3007 write('#define UNUSED_PARAMETER(x) (void)(x)', file=self.outFile)
3008 write('#endif // UNUSED_PARAMETER', file=self.outFile)
Dustin Gravesb83fc2d2016-05-04 12:56:08 -06003009 #
3010 # Namespace
3011 self.newline()
3012 write('namespace parameter_validation {', file = self.outFile)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003013 def endFile(self):
3014 # C-specific
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003015 self.newline()
Dustin Gravesb83fc2d2016-05-04 12:56:08 -06003016 # Namespace
3017 write('} // namespace parameter_validation', file = self.outFile)
3018 # Finish C++ wrapper and multiple inclusion protection
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003019 if (self.genOpts.protectFile and self.genOpts.filename):
3020 self.newline()
3021 write('#endif', file=self.outFile)
3022 # Finish processing in superclass
3023 OutputGenerator.endFile(self)
3024 def beginFeature(self, interface, emit):
3025 # Start processing in superclass
3026 OutputGenerator.beginFeature(self, interface, emit)
3027 # C-specific
3028 # Accumulate includes, defines, types, enums, function pointer typedefs,
3029 # end function prototypes separately for this feature. They're only
3030 # printed in endFeature().
Dustin Graves6e4c2be2016-07-19 13:17:35 -06003031 self.headerVersion = None
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003032 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
Dustin Graveseec48bf2016-03-02 18:23:29 -07003033 self.structNames = []
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003034 self.stypes = []
3035 self.structTypes = dict()
Dustin Graves20fd66f2016-04-18 18:33:21 -06003036 self.handleTypes = set()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003037 self.commands = []
Dustin Graveseec48bf2016-03-02 18:23:29 -07003038 self.structMembers = []
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003039 self.validatedStructs = dict()
Dustin Graves29148ff2016-03-23 19:44:00 -06003040 self.enumRanges = dict()
Dustin Gravesc900f572016-05-16 11:07:59 -06003041 self.flags = set()
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003042 self.flagBits = dict()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003043 def endFeature(self):
3044 # C-specific
3045 # Actually write the interface to the output file.
3046 if (self.emit):
3047 self.newline()
3048 # If type declarations are needed by other features based on
3049 # this one, it may be necessary to suppress the ExtraProtect,
3050 # or move it below the 'for section...' loop.
3051 if (self.featureExtraProtect != None):
3052 write('#ifdef', self.featureExtraProtect, file=self.outFile)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003053 # Generate the struct member checking code from the captured data
Dustin Graveseec48bf2016-03-02 18:23:29 -07003054 self.processStructMemberData()
3055 # Generate the command parameter checking code from the captured data
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003056 self.processCmdData()
Dustin Graves6e4c2be2016-07-19 13:17:35 -06003057 # Write the declaration for the HeaderVersion
3058 if self.headerVersion:
3059 write('const uint32_t GeneratedHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
3060 self.newline()
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003061 # Write the declarations for the VkFlags values combining all flag bits
3062 for flag in sorted(self.flags):
Dustin Gravesc900f572016-05-16 11:07:59 -06003063 flagBits = flag.replace('Flags', 'FlagBits')
3064 if flagBits in self.flagBits:
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003065 bits = self.flagBits[flagBits]
3066 decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
3067 for bit in bits[1:]:
3068 decl += '|' + bit
3069 decl += ';'
3070 write(decl, file=self.outFile)
3071 self.newline()
3072 # Write the parameter validation code to the file
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003073 if (self.sections['command']):
3074 if (self.genOpts.protectProto):
3075 write(self.genOpts.protectProto,
3076 self.genOpts.protectProtoStr, file=self.outFile)
3077 write('\n'.join(self.sections['command']), end='', file=self.outFile)
3078 if (self.featureExtraProtect != None):
3079 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
3080 else:
3081 self.newline()
3082 # Finish processing in superclass
3083 OutputGenerator.endFeature(self)
3084 #
3085 # Append a definition to the specified section
3086 def appendSection(self, section, text):
3087 # self.sections[section].append('SECTION: ' + section + '\n')
3088 self.sections[section].append(text)
3089 #
3090 # Type generation
3091 def genType(self, typeinfo, name):
3092 OutputGenerator.genType(self, typeinfo, name)
3093 typeElem = typeinfo.elem
3094 # If the type is a struct type, traverse the imbedded <member> tags
3095 # generating a structure. Otherwise, emit the tag text.
3096 category = typeElem.get('category')
3097 if (category == 'struct' or category == 'union'):
Dustin Graveseec48bf2016-03-02 18:23:29 -07003098 self.structNames.append(name)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003099 self.genStruct(typeinfo, name)
Dustin Graves20fd66f2016-04-18 18:33:21 -06003100 elif (category == 'handle'):
3101 self.handleTypes.add(name)
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003102 elif (category == 'bitmask'):
Dustin Gravesc900f572016-05-16 11:07:59 -06003103 self.flags.add(name)
Dustin Graves6e4c2be2016-07-19 13:17:35 -06003104 elif (category == 'define'):
3105 if name == 'VK_HEADER_VERSION':
3106 nameElem = typeElem.find('name')
3107 self.headerVersion = noneStr(nameElem.tail).strip()
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003108 #
3109 # Struct parameter check generation.
3110 # This is a special case of the <type> tag where the contents are
3111 # interpreted as a set of <member> tags instead of freeform C
3112 # C type declarations. The <member> tags are just like <param>
3113 # tags - they are a declaration of a struct or union member.
3114 # Only simple member declarations are supported (no nested
3115 # structs etc.)
3116 def genStruct(self, typeinfo, typeName):
3117 OutputGenerator.genStruct(self, typeinfo, typeName)
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003118 conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
Dustin Graveseec48bf2016-03-02 18:23:29 -07003119 members = typeinfo.elem.findall('.//member')
3120 #
3121 # Iterate over members once to get length parameters for arrays
3122 lens = set()
3123 for member in members:
3124 len = self.getLen(member)
3125 if len:
3126 lens.add(len)
3127 #
3128 # Generate member info
3129 membersInfo = []
3130 for member in members:
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003131 # Get the member's type and name
Dustin Graveseec48bf2016-03-02 18:23:29 -07003132 info = self.getTypeNameTuple(member)
3133 type = info[0]
3134 name = info[1]
3135 stypeValue = ''
Dustin Graves29148ff2016-03-23 19:44:00 -06003136 cdecl = self.makeCParamDecl(member, 0)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003137 # Process VkStructureType
3138 if type == 'VkStructureType':
3139 # Extract the required struct type value from the comments
3140 # embedded in the original text defining the 'typeinfo' element
3141 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
Dustin Graveseec48bf2016-03-02 18:23:29 -07003142 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003143 if result:
3144 value = result.group(0)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003145 else:
Dustin Graves94f19142016-05-10 16:44:16 -06003146 value = self.genVkStructureType(typeName)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003147 # Store the required type value
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003148 self.structTypes[typeName] = self.StructType(name=name, value=value)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003149 #
3150 # Store pointer/array/string info
3151 # Check for parameter name in lens set
3152 iscount = False
3153 if name in lens:
3154 iscount = True
3155 # The pNext members are not tagged as optional, but are treated as
3156 # optional for parameter NULL checks. Static array members
3157 # are also treated as optional to skip NULL pointer validation, as
3158 # they won't be NULL.
3159 isstaticarray = self.paramIsStaticArray(member)
3160 isoptional = False
3161 if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
3162 isoptional = True
3163 membersInfo.append(self.CommandParam(type=type, name=name,
3164 ispointer=self.paramIsPointer(member),
3165 isstaticarray=isstaticarray,
Dustin Graves29148ff2016-03-23 19:44:00 -06003166 isbool=True if type == 'VkBool32' else False,
3167 israngedenum=True if type in self.enumRanges else False,
3168 isconst=True if 'const' in cdecl else False,
Dustin Graveseec48bf2016-03-02 18:23:29 -07003169 isoptional=isoptional,
3170 iscount=iscount,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003171 noautovalidity=True if member.attrib.get('noautovalidity') is not None else False,
Dustin Graveseec48bf2016-03-02 18:23:29 -07003172 len=self.getLen(member),
Dustin Graves58c2f662016-03-08 17:48:20 -07003173 extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None,
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003174 condition=conditions[name] if conditions and name in conditions else None,
Dustin Graves29148ff2016-03-23 19:44:00 -06003175 cdecl=cdecl))
Dustin Graveseec48bf2016-03-02 18:23:29 -07003176 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003177 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003178 # Capture group (e.g. C "enum" type) info to be used for
3179 # param check code generation.
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003180 # These are concatenated together with other types.
3181 def genGroup(self, groupinfo, groupName):
3182 OutputGenerator.genGroup(self, groupinfo, groupName)
Dustin Graves29148ff2016-03-23 19:44:00 -06003183 groupElem = groupinfo.elem
3184 #
3185 # Store the sType values
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003186 if groupName == 'VkStructureType':
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003187 for elem in groupElem.findall('enum'):
Dustin Graves29148ff2016-03-23 19:44:00 -06003188 self.stypes.append(elem.get('name'))
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003189 elif 'FlagBits' in groupName:
3190 bits = []
3191 for elem in groupElem.findall('enum'):
3192 bits.append(elem.get('name'))
3193 if bits:
3194 self.flagBits[groupName] = bits
Dustin Graves29148ff2016-03-23 19:44:00 -06003195 else:
3196 # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
3197 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
3198 expandPrefix = expandName
3199 expandSuffix = ''
3200 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
3201 if expandSuffixMatch:
3202 expandSuffix = '_' + expandSuffixMatch.group()
3203 # Strip off the suffix from the prefix
3204 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
3205 isEnum = ('FLAG_BITS' not in expandPrefix)
3206 if isEnum:
3207 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003208 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003209 # Capture command parameter info to be used for param
3210 # check code generation.
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003211 def genCmd(self, cmdinfo, name):
3212 OutputGenerator.genCmd(self, cmdinfo, name)
3213 if name not in self.blacklist:
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003214 params = cmdinfo.elem.findall('param')
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003215 # Get list of array lengths
3216 lens = set()
3217 for param in params:
3218 len = self.getLen(param)
3219 if len:
3220 lens.add(len)
3221 # Get param info
3222 paramsInfo = []
3223 for param in params:
3224 paramInfo = self.getTypeNameTuple(param)
Dustin Graves29148ff2016-03-23 19:44:00 -06003225 cdecl = self.makeCParamDecl(param, 0)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003226 # Check for parameter name in lens set
3227 iscount = False
3228 if paramInfo[1] in lens:
3229 iscount = True
3230 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
3231 ispointer=self.paramIsPointer(param),
3232 isstaticarray=self.paramIsStaticArray(param),
Dustin Graves29148ff2016-03-23 19:44:00 -06003233 isbool=True if paramInfo[0] == 'VkBool32' else False,
3234 israngedenum=True if paramInfo[0] in self.enumRanges else False,
3235 isconst=True if 'const' in cdecl else False,
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003236 isoptional=self.paramIsOptional(param),
3237 iscount=iscount,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003238 noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003239 len=self.getLen(param),
Dustin Graves58c2f662016-03-08 17:48:20 -07003240 extstructs=None,
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003241 condition=None,
Dustin Graves29148ff2016-03-23 19:44:00 -06003242 cdecl=cdecl))
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003243 self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0]))
3244 #
3245 # Check if the parameter passed in is a pointer
3246 def paramIsPointer(self, param):
Dustin Graveseec48bf2016-03-02 18:23:29 -07003247 ispointer = 0
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003248 paramtype = param.find('type')
Dustin Graveseec48bf2016-03-02 18:23:29 -07003249 if (paramtype.tail is not None) and ('*' in paramtype.tail):
3250 ispointer = paramtype.tail.count('*')
Dustin Graves27a912a2016-03-07 17:52:14 -07003251 elif paramtype.text[:4] == 'PFN_':
3252 # Treat function pointer typedefs as a pointer to a single value
3253 ispointer = 1
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003254 return ispointer
3255 #
3256 # Check if the parameter passed in is a static array
3257 def paramIsStaticArray(self, param):
Dustin Graveseec48bf2016-03-02 18:23:29 -07003258 isstaticarray = 0
3259 paramname = param.find('name')
3260 if (paramname.tail is not None) and ('[' in paramname.tail):
3261 isstaticarray = paramname.tail.count('[')
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003262 return isstaticarray
3263 #
3264 # Check if the parameter passed in is optional
3265 # Returns a list of Boolean values for comma separated len attributes (len='false,true')
3266 def paramIsOptional(self, param):
3267 # See if the handle is optional
3268 isoptional = False
3269 # Simple, if it's optional, return true
3270 optString = param.attrib.get('optional')
3271 if optString:
3272 if optString == 'true':
3273 isoptional = True
3274 elif ',' in optString:
3275 opts = []
3276 for opt in optString.split(','):
3277 val = opt.strip()
3278 if val == 'true':
3279 opts.append(True)
3280 elif val == 'false':
3281 opts.append(False)
3282 else:
3283 print('Unrecognized len attribute value',val)
3284 isoptional = opts
3285 return isoptional
3286 #
Dustin Graves20fd66f2016-04-18 18:33:21 -06003287 # Check if the handle passed in is optional
3288 # Uses the same logic as ValidityOutputGenerator.isHandleOptional
3289 def isHandleOptional(self, param, lenParam):
3290 # Simple, if it's optional, return true
3291 if param.isoptional:
3292 return True
3293 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
3294 if param.noautovalidity:
3295 return True
3296 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
3297 if lenParam and lenParam.isoptional:
3298 return True
3299 return False
3300 #
Dustin Graves94f19142016-05-10 16:44:16 -06003301 # Generate a VkStructureType based on a structure typename
3302 def genVkStructureType(self, typename):
3303 # Add underscore between lowercase then uppercase
3304 value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
3305 # Change to uppercase
3306 value = value.upper()
3307 # Add STRUCTURE_TYPE_
3308 return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
3309 #
3310 # Get the cached VkStructureType value for the specified struct typename, or generate a VkStructureType
3311 # value assuming the struct is defined by a different feature
3312 def getStructType(self, typename):
3313 value = None
3314 if typename in self.structTypes:
3315 value = self.structTypes[typename].value
3316 else:
3317 value = self.genVkStructureType(typename)
Dustin Gravesc900f572016-05-16 11:07:59 -06003318 self.logMsg('diag', 'ParameterValidation: Generating {} for {} structure type that was not defined by the current feature'.format(value, typename))
Dustin Graves94f19142016-05-10 16:44:16 -06003319 return value
3320 #
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003321 # Retrieve the value of the len tag
3322 def getLen(self, param):
Dustin Graveseec48bf2016-03-02 18:23:29 -07003323 result = None
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003324 len = param.attrib.get('len')
3325 if len and len != 'null-terminated':
Dustin Graveseec48bf2016-03-02 18:23:29 -07003326 # For string arrays, 'len' can look like 'count,null-terminated',
3327 # indicating that we have a null terminated array of strings. We
3328 # strip the null-terminated from the 'len' field and only return
3329 # the parameter specifying the string count
3330 if 'null-terminated' in len:
3331 result = len.split(',')[0]
3332 else:
3333 result = len
Mark Lobodzinski3b34af02016-09-27 13:08:15 -06003334 result = str(result).replace('::', '->')
Dustin Graveseec48bf2016-03-02 18:23:29 -07003335 return result
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003336 #
3337 # Retrieve the type and name for a parameter
3338 def getTypeNameTuple(self, param):
3339 type = ''
3340 name = ''
3341 for elem in param:
3342 if elem.tag == 'type':
3343 type = noneStr(elem.text)
3344 elif elem.tag == 'name':
3345 name = noneStr(elem.text)
3346 return (type, name)
3347 #
3348 # Find a named parameter in a parameter list
3349 def getParamByName(self, params, name):
3350 for param in params:
3351 if param.name == name:
3352 return param
3353 return None
Dustin Graveseec48bf2016-03-02 18:23:29 -07003354 #
Dustin Graves3ff520c2016-03-28 16:17:38 -06003355 # Extract length values from latexmath. Currently an inflexible solution that looks for specific
3356 # patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
3357 def parseLateXMath(self, source):
3358 name = 'ERROR'
3359 decoratedName = 'ERROR'
3360 if 'mathit' in source:
3361 # Matches expressions similar to 'latexmath:[$\lceil{\mathit{rasterizationSamples} \over 32}\rceil$]'
3362 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)
3363 if not match or match.group(1) != match.group(4):
3364 raise 'Unrecognized latexmath expression'
3365 name = match.group(2)
3366 decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
3367 else:
3368 # Matches expressions similar to 'latexmath : [$dataSize \over 4$]'
3369 match = re.match(r'latexmath\s*\:\s*\[\s*\$\s*(\w+)\s*\\over\s*(\d+)\s*\$\s*\]', source)
3370 name = match.group(1)
3371 decoratedName = '{}/{}'.format(*match.group(1, 2))
3372 return name, decoratedName
3373 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003374 # Get the length paramater record for the specified parameter name
3375 def getLenParam(self, params, name):
3376 lenParam = None
3377 if name:
3378 if '->' in name:
3379 # The count is obtained by dereferencing a member of a struct parameter
Dustin Graves29148ff2016-03-23 19:44:00 -06003380 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003381 isstaticarray=None, isoptional=False, type=None, noautovalidity=False, len=None, extstructs=None,
3382 condition=None, cdecl=None)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003383 elif 'latexmath' in name:
Dustin Graves3ff520c2016-03-28 16:17:38 -06003384 lenName, decoratedName = self.parseLateXMath(name)
3385 lenParam = self.getParamByName(params, lenName)
3386 # TODO: Zero-check the result produced by the equation?
3387 # Copy the stored len parameter entry and overwrite the name with the processed latexmath equation
3388 #param = self.getParamByName(params, lenName)
3389 #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer,
3390 # isoptional=param.isoptional, type=param.type, len=param.len,
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003391 # isstaticarray=param.isstaticarray, extstructs=param.extstructs,
Dustin Graves20fd66f2016-04-18 18:33:21 -06003392 # noautovalidity=True, condition=None, cdecl=param.cdecl)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003393 else:
3394 lenParam = self.getParamByName(params, name)
3395 return lenParam
3396 #
Mark Lobodzinski739391a2016-03-17 15:08:18 -06003397 # Convert a vulkan.h command declaration into a parameter_validation.h definition
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003398 def getCmdDef(self, cmd):
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003399 #
3400 # Strip the trailing ';' and split into individual lines
3401 lines = cmd.cdecl[:-1].split('\n')
3402 # Replace Vulkan prototype
Dustin Graves080069b2016-04-05 13:48:15 -06003403 lines[0] = 'static bool parameter_validation_' + cmd.name + '('
Dustin Graves842621d2016-03-03 14:17:08 -07003404 # Replace the first argument with debug_report_data, when the first
3405 # argument is a handle (not vkCreateInstance)
3406 reportData = ' debug_report_data*'.ljust(self.genOpts.alignFuncParam) + 'report_data,'
3407 if cmd.name != 'vkCreateInstance':
3408 lines[1] = reportData
3409 else:
3410 lines.insert(1, reportData)
Dustin Gravesdfa6acf2016-02-11 10:10:14 -07003411 return '\n'.join(lines)
3412 #
Dustin Graves35328c12016-02-29 13:35:07 -07003413 # Generate the code to check for a NULL dereference before calling the
3414 # validation function
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003415 def genCheckedLengthCall(self, name, exprs):
Dustin Graves35328c12016-02-29 13:35:07 -07003416 count = name.count('->')
3417 if count:
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003418 checkedExpr = []
3419 localIndent = ''
Dustin Graves35328c12016-02-29 13:35:07 -07003420 elements = name.split('->')
3421 # Open the if expression blocks
3422 for i in range(0, count):
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003423 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
Dustin Graves35328c12016-02-29 13:35:07 -07003424 localIndent = self.incIndent(localIndent)
3425 # Add the validation expression
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003426 for expr in exprs:
3427 checkedExpr.append(localIndent + expr)
Dustin Graves35328c12016-02-29 13:35:07 -07003428 # Close the if blocks
3429 for i in range(0, count):
3430 localIndent = self.decIndent(localIndent)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003431 checkedExpr.append(localIndent + '}\n')
3432 return [checkedExpr]
Dustin Graves35328c12016-02-29 13:35:07 -07003433 # No if statements were required
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003434 return exprs
3435 #
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003436 # Generate code to check for a specific condition before executing validation code
3437 def genConditionalCall(self, prefix, condition, exprs):
3438 checkedExpr = []
3439 localIndent = ''
3440 formattedCondition = condition.format(prefix)
3441 checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
3442 checkedExpr.append(localIndent + '{\n')
3443 localIndent = self.incIndent(localIndent)
3444 for expr in exprs:
3445 checkedExpr.append(localIndent + expr)
3446 localIndent = self.decIndent(localIndent)
3447 checkedExpr.append(localIndent + '}\n')
3448 return [checkedExpr]
3449 #
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003450 # Generate the sType check string
Dustin Graves32b3c692016-07-22 13:20:44 -06003451 def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003452 checkExpr = []
3453 stype = self.structTypes[value.type]
3454 if lenValue:
3455 # This is an array with a pointer to a count value
3456 if lenValue.ispointer:
3457 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Dustin Graves32b3c692016-07-22 13:20:44 -06003458 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format(
3459 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003460 # This is an array with an integer count value
3461 else:
Dustin Graves32b3c692016-07-22 13:20:44 -06003462 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {});\n'.format(
3463 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003464 # This is an individual struct
3465 else:
Dustin Graves32b3c692016-07-22 13:20:44 -06003466 checkExpr.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {});\n'.format(
3467 funcPrintName, valuePrintName, prefix, valueRequired, vn=value.name, sv=stype.value, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003468 return checkExpr
3469 #
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003470 # Generate the handle check string
Dustin Graves32b3c692016-07-22 13:20:44 -06003471 def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
Dustin Graves20fd66f2016-04-18 18:33:21 -06003472 checkExpr = []
3473 if lenValue:
Dustin Graves20fd66f2016-04-18 18:33:21 -06003474 if lenValue.ispointer:
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003475 # This is assumed to be an output array with a pointer to a count value
Dustin Graves20fd66f2016-04-18 18:33:21 -06003476 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
Dustin Graves20fd66f2016-04-18 18:33:21 -06003477 else:
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003478 # This is an array with an integer count value
Dustin Graves32b3c692016-07-22 13:20:44 -06003479 checkExpr.append('skipCall |= validate_handle_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
3480 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
Dustin Graves20fd66f2016-04-18 18:33:21 -06003481 else:
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003482 # This is assumed to be an output handle pointer
Dustin Graves20fd66f2016-04-18 18:33:21 -06003483 raise('Unsupported parameter validation case: Output handles are not NULL checked')
3484 return checkExpr
3485 #
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003486 # Generate check string for an array of VkFlags values
Dustin Graves32b3c692016-07-22 13:20:44 -06003487 def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003488 checkExpr = []
3489 flagBitsName = value.type.replace('Flags', 'FlagBits')
3490 if not flagBitsName in self.flagBits:
3491 raise('Unsupported parameter validation case: array of reserved VkFlags')
3492 else:
3493 allFlags = 'All' + flagBitsName
Dustin Graves32b3c692016-07-22 13:20:44 -06003494 checkExpr.append('skipCall |= validate_flags_array(report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcPrintName, lenPrintName, valuePrintName, flagBitsName, allFlags, lenValue.name, value.name, lenValueRequired, valueRequired, pf=prefix, **postProcSpec))
Dustin Graves9c6b62b2016-04-26 15:37:10 -06003495 return checkExpr
3496 #
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003497 # Generate pNext check string
Dustin Graves32b3c692016-07-22 13:20:44 -06003498 def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec):
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003499 checkExpr = []
3500 # Generate an array of acceptable VkStructureType values for pNext
3501 extStructCount = 0
3502 extStructVar = 'NULL'
3503 extStructNames = 'NULL'
3504 if value.extstructs:
3505 structs = value.extstructs.split(',')
Dustin Graves94f19142016-05-10 16:44:16 -06003506 checkExpr.append('const VkStructureType allowedStructs[] = {' + ', '.join([self.getStructType(s) for s in structs]) + '};\n')
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003507 extStructCount = 'ARRAY_SIZE(allowedStructs)'
3508 extStructVar = 'allowedStructs'
3509 extStructNames = '"' + ', '.join(structs) + '"'
Dustin Graves32b3c692016-07-22 13:20:44 -06003510 checkExpr.append('skipCall |= validate_struct_pnext(report_data, "{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedHeaderVersion);\n'.format(
3511 funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003512 return checkExpr
3513 #
3514 # Generate the pointer check string
Dustin Graves32b3c692016-07-22 13:20:44 -06003515 def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003516 checkExpr = []
3517 if lenValue:
3518 # This is an array with a pointer to a count value
3519 if lenValue.ispointer:
3520 # If count and array parameters are optional, there will be no validation
3521 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
3522 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Dustin Graves32b3c692016-07-22 13:20:44 -06003523 checkExpr.append('skipCall |= validate_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {});\n'.format(
3524 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003525 # This is an array with an integer count value
3526 else:
3527 # If count and array parameters are optional, there will be no validation
3528 if valueRequired == 'true' or lenValueRequired == 'true':
3529 # Arrays of strings receive special processing
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003530 validationFuncName = 'validate_array' if value.type != 'char' else 'validate_string_array'
Dustin Graves32b3c692016-07-22 13:20:44 -06003531 checkExpr.append('skipCall |= {}(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
3532 validationFuncName, funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003533 if checkExpr:
3534 if lenValue and ('->' in lenValue.name):
3535 # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
3536 checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
3537 # This is an individual struct that is not allowed to be NULL
3538 elif not value.isoptional:
3539 # Function pointers need a reinterpret_cast to void*
3540 if value.type[:4] == 'PFN_':
Dustin Graves32b3c692016-07-22 13:20:44 -06003541 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", {ppp}"{}"{pps}, reinterpret_cast<const void*>({}{}));\n'.format(funcPrintName, valuePrintName, prefix, value.name, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003542 else:
Dustin Graves32b3c692016-07-22 13:20:44 -06003543 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcPrintName, valuePrintName, prefix, value.name, **postProcSpec))
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003544 return checkExpr
Dustin Graveseec48bf2016-03-02 18:23:29 -07003545 #
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003546 # Process struct member validation code, performing name suibstitution if required
Dustin Graves32b3c692016-07-22 13:20:44 -06003547 def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
3548 # Build format specifier list
3549 kwargs = {}
3550 if '{postProcPrefix}' in line:
3551 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3552 if type(memberDisplayNamePrefix) is tuple:
3553 kwargs['postProcPrefix'] = 'ParameterName('
3554 else:
3555 kwargs['postProcPrefix'] = postProcSpec['ppp']
3556 if '{postProcSuffix}' in line:
3557 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3558 if type(memberDisplayNamePrefix) is tuple:
3559 kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
3560 else:
3561 kwargs['postProcSuffix'] = postProcSpec['pps']
3562 if '{postProcInsert}' in line:
3563 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3564 if type(memberDisplayNamePrefix) is tuple:
3565 kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
3566 else:
3567 kwargs['postProcInsert'] = postProcSpec['ppi']
3568 if '{funcName}' in line:
3569 kwargs['funcName'] = funcName
3570 if '{valuePrefix}' in line:
3571 kwargs['valuePrefix'] = memberNamePrefix
3572 if '{displayNamePrefix}' in line:
3573 # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
3574 if type(memberDisplayNamePrefix) is tuple:
3575 kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
3576 else:
3577 kwargs['displayNamePrefix'] = memberDisplayNamePrefix
3578
3579 if kwargs:
3580 # Need to escape the C++ curly braces
3581 if 'IndexVector' in line:
3582 line = line.replace('IndexVector{ ', 'IndexVector{{ ')
3583 line = line.replace(' }),', ' }}),')
3584 return line.format(**kwargs)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003585 return line
3586 #
3587 # Process struct validation code for inclusion in function or parent struct validation code
Dustin Graves32b3c692016-07-22 13:20:44 -06003588 def expandStructCode(self, lines, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003589 for line in lines:
3590 if output:
3591 output[-1] += '\n'
3592 if type(line) is list:
3593 for sub in line:
Dustin Graves32b3c692016-07-22 13:20:44 -06003594 output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003595 else:
Dustin Graves32b3c692016-07-22 13:20:44 -06003596 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003597 return output
3598 #
3599 # Process struct pointer/array validation code, perfoeming name substitution if required
Dustin Graves32b3c692016-07-22 13:20:44 -06003600 def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003601 expr = []
3602 expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
3603 expr.append('{')
3604 indent = self.incIndent(None)
3605 if lenValue:
3606 # Need to process all elements in the array
3607 indexName = lenValue.name.replace('Count', 'Index')
3608 expr[-1] += '\n'
3609 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
3610 expr.append(indent + '{')
3611 indent = self.incIndent(indent)
3612 # Prefix for value name to display in error message
3613 memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
Dustin Graves32b3c692016-07-22 13:20:44 -06003614 memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003615 else:
3616 memberNamePrefix = '{}{}->'.format(prefix, value.name)
3617 memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
3618 #
3619 # Expand the struct validation lines
Dustin Graves32b3c692016-07-22 13:20:44 -06003620 expr = self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003621 #
3622 if lenValue:
3623 # Close if and for scopes
3624 indent = self.decIndent(indent)
3625 expr.append(indent + '}\n')
3626 expr.append('}\n')
3627 return expr
3628 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003629 # Generate the parameter checking code
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003630 def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
3631 lines = [] # Generated lines of code
3632 unused = [] # Unused variable names
Dustin Graveseec48bf2016-03-02 18:23:29 -07003633 for value in values:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003634 usedLines = []
Dustin Graves29148ff2016-03-23 19:44:00 -06003635 lenParam = None
3636 #
Dustin Graves32b3c692016-07-22 13:20:44 -06003637 # Prefix and suffix for post processing of parameter names for struct members. Arrays of structures need special processing to include the array index in the full parameter name.
3638 postProcSpec = {}
3639 postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
3640 postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
3641 postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
3642 #
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003643 # 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 -06003644 valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003645 #
3646 # Check for NULL pointers, ignore the inout count parameters that
3647 # will be validated with their associated array
3648 if (value.ispointer or value.isstaticarray) and not value.iscount:
3649 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003650 # Parameters for function argument generation
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003651 req = 'true' # Paramerter cannot be NULL
3652 cpReq = 'true' # Count pointer cannot be NULL
3653 cvReq = 'true' # Count value cannot be 0
Dustin Graves29148ff2016-03-23 19:44:00 -06003654 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
Dustin Graveseec48bf2016-03-02 18:23:29 -07003655 #
3656 # Generate required/optional parameter strings for the pointer and count values
3657 if value.isoptional:
Dustin Graves080069b2016-04-05 13:48:15 -06003658 req = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003659 if value.len:
3660 # The parameter is an array with an explicit count parameter
3661 lenParam = self.getLenParam(values, value.len)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003662 lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
Dustin Graveseec48bf2016-03-02 18:23:29 -07003663 if lenParam.ispointer:
3664 # Count parameters that are pointers are inout
3665 if type(lenParam.isoptional) is list:
3666 if lenParam.isoptional[0]:
Dustin Graves080069b2016-04-05 13:48:15 -06003667 cpReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003668 if lenParam.isoptional[1]:
Dustin Graves080069b2016-04-05 13:48:15 -06003669 cvReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003670 else:
3671 if lenParam.isoptional:
Dustin Graves080069b2016-04-05 13:48:15 -06003672 cpReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003673 else:
3674 if lenParam.isoptional:
Dustin Graves080069b2016-04-05 13:48:15 -06003675 cvReq = 'false'
Dustin Graveseec48bf2016-03-02 18:23:29 -07003676 #
Dustin Gravesc900f572016-05-16 11:07:59 -06003677 # The parameter will not be processes when tagged as 'noautovalidity'
3678 # For the pointer to struct case, the struct pointer will not be validated, but any
3679 # members not tagged as 'noatuvalidity' will be validated
3680 if value.noautovalidity:
3681 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
3682 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
Dustin Graveseec48bf2016-03-02 18:23:29 -07003683 else:
Dustin Gravesc900f572016-05-16 11:07:59 -06003684 #
3685 # If this is a pointer to a struct with an sType field, verify the type
3686 if value.type in self.structTypes:
Dustin Graves32b3c692016-07-22 13:20:44 -06003687 usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
Dustin Gravesc900f572016-05-16 11:07:59 -06003688 # 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
3689 elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
Dustin Graves32b3c692016-07-22 13:20:44 -06003690 usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
Dustin Gravesc900f572016-05-16 11:07:59 -06003691 elif value.type in self.flags and value.isconst:
Dustin Graves32b3c692016-07-22 13:20:44 -06003692 usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
Dustin Gravesc900f572016-05-16 11:07:59 -06003693 elif value.isbool and value.isconst:
Dustin Graves32b3c692016-07-22 13:20:44 -06003694 usedLines.append('skipCall |= validate_bool32_array(report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003695 elif value.israngedenum and value.isconst:
3696 enumRange = self.enumRanges[value.type]
Dustin Graves32b3c692016-07-22 13:20:44 -06003697 usedLines.append('skipCall |= validate_ranged_enum_array(report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, value.type, enumRange[0], enumRange[1], lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003698 elif value.name == 'pNext':
3699 # We need to ignore VkDeviceCreateInfo and VkInstanceCreateInfo, as the loader manipulates them in a way that is not documented in vk.xml
3700 if not structTypeName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']:
Dustin Graves32b3c692016-07-22 13:20:44 -06003701 usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec)
Dustin Gravesc900f572016-05-16 11:07:59 -06003702 else:
Dustin Graves32b3c692016-07-22 13:20:44 -06003703 usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
Dustin Graves629259b2016-05-30 16:14:27 -06003704 #
3705 # If this is a pointer to a struct (input), see if it contains members that need to be checked
3706 if value.type in self.validatedStructs and value.isconst:
Dustin Graves32b3c692016-07-22 13:20:44 -06003707 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003708 # Non-pointer types
Dustin Gravesc900f572016-05-16 11:07:59 -06003709 else:
3710 #
3711 # The parameter will not be processes when tagged as 'noautovalidity'
3712 # For the struct case, the struct type will not be validated, but any
3713 # members not tagged as 'noatuvalidity' will be validated
3714 if value.noautovalidity:
3715 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
3716 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
3717 else:
3718 if value.type in self.structTypes:
3719 stype = self.structTypes[value.type]
Dustin Graves32b3c692016-07-22 13:20:44 -06003720 usedLines.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false);\n'.format(
3721 funcName, valueDisplayName, valuePrefix, vn=value.name, sv=stype.value, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003722 elif value.type in self.handleTypes:
3723 if not self.isHandleOptional(value, None):
Dustin Graves32b3c692016-07-22 13:20:44 -06003724 usedLines.append('skipCall |= validate_required_handle(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003725 elif value.type in self.flags:
3726 flagBitsName = value.type.replace('Flags', 'FlagBits')
3727 if not flagBitsName in self.flagBits:
Dustin Graves32b3c692016-07-22 13:20:44 -06003728 usedLines.append('skipCall |= validate_reserved_flags(report_data, "{}", {ppp}"{}"{pps}, {pf}{});\n'.format(funcName, valueDisplayName, value.name, pf=valuePrefix, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003729 else:
3730 flagsRequired = 'false' if value.isoptional else 'true'
3731 allFlagsName = 'All' + flagBitsName
Dustin Graves32b3c692016-07-22 13:20:44 -06003732 usedLines.append('skipCall |= validate_flags(report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, pf=valuePrefix, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003733 elif value.isbool:
Dustin Graves32b3c692016-07-22 13:20:44 -06003734 usedLines.append('skipCall |= validate_bool32(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Dustin Gravesc900f572016-05-16 11:07:59 -06003735 elif value.israngedenum:
3736 enumRange = self.enumRanges[value.type]
Dustin Graves32b3c692016-07-22 13:20:44 -06003737 usedLines.append('skipCall |= validate_ranged_enum(report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {}, {}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], valuePrefix, value.name, **postProcSpec))
Dustin Graves629259b2016-05-30 16:14:27 -06003738 #
3739 # If this is a struct, see if it contains members that need to be checked
3740 if value.type in self.validatedStructs:
3741 memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
3742 memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
Dustin Graves32b3c692016-07-22 13:20:44 -06003743 usedLines.append(self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
Dustin Graveseec48bf2016-03-02 18:23:29 -07003744 #
3745 # Append the parameter check to the function body for the current command
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003746 if usedLines:
Dustin Gravesff4d7bc2016-04-15 18:06:14 -06003747 # Apply special conditional checks
3748 if value.condition:
3749 usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003750 lines += usedLines
Dustin Graves842621d2016-03-03 14:17:08 -07003751 elif not value.iscount:
Dustin Graves29148ff2016-03-23 19:44:00 -06003752 # If no expression was generated for this value, it is unreferenced by the validation function, unless
3753 # it is an array count, which is indirectly referenced for array valiadation.
Dustin Graves842621d2016-03-03 14:17:08 -07003754 unused.append(value.name)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003755 return lines, unused
Dustin Graveseec48bf2016-03-02 18:23:29 -07003756 #
Dustin Graveseec48bf2016-03-02 18:23:29 -07003757 # Generate the struct member check code from the captured data
3758 def processStructMemberData(self):
3759 indent = self.incIndent(None)
3760 for struct in self.structMembers:
Dustin Graves29148ff2016-03-23 19:44:00 -06003761 #
3762 # 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 -06003763 lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003764 if lines:
Dustin Gravesb2e9ff72016-04-14 15:03:31 -06003765 self.validatedStructs[struct.name] = lines
Dustin Graveseec48bf2016-03-02 18:23:29 -07003766 #
3767 # Generate the command param check code from the captured data
3768 def processCmdData(self):
3769 indent = self.incIndent(None)
3770 for command in self.commands:
Dustin Graves20fd66f2016-04-18 18:33:21 -06003771 # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
3772 startIndex = 0 if command.name == 'vkCreateInstance' else 1
3773 lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003774 if lines:
Dustin Graveseec48bf2016-03-02 18:23:29 -07003775 cmdDef = self.getCmdDef(command) + '\n'
3776 cmdDef += '{\n'
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003777 # Process unused parameters, Ignoring the first dispatch handle parameter, which is not
3778 # processed by parameter_validation (except for vkCreateInstance, which does not have a
3779 # handle as its first parameter)
3780 if unused:
Dustin Graves20fd66f2016-04-18 18:33:21 -06003781 for name in unused:
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003782 cmdDef += indent + 'UNUSED_PARAMETER({});\n'.format(name)
Dustin Graves20fd66f2016-04-18 18:33:21 -06003783 if len(unused) > 0:
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003784 cmdDef += '\n'
Dustin Graves080069b2016-04-05 13:48:15 -06003785 cmdDef += indent + 'bool skipCall = false;\n'
Dustin Gravesd0618ad2016-04-11 16:06:25 -06003786 for line in lines:
3787 cmdDef += '\n'
3788 if type(line) is list:
3789 for sub in line:
3790 cmdDef += indent + sub
3791 else:
3792 cmdDef += indent + line
Dustin Graveseec48bf2016-03-02 18:23:29 -07003793 cmdDef += '\n'
3794 cmdDef += indent + 'return skipCall;\n'
3795 cmdDef += '}\n'
3796 self.appendSection('command', cmdDef)
Mark Lobodzinskidc3bd852016-09-06 16:12:23 -06003797
3798# UniqueObjectsOutputGenerator - subclass of OutputGenerator.
3799# Generates unique objects layer non-dispatchable handle-wrapping code.
3800#
3801# ---- methods ----
3802# UniqueObjectsOutputGenerator(errFile, warnFile, diagFile) - args as for OutputGenerator. Defines additional internal state.
3803# ---- methods overriding base class ----
3804# beginFile(genOpts)
3805# endFile()
3806# beginFeature(interface, emit)
3807# endFeature()
3808# genCmd(cmdinfo)
3809# genStruct()
3810# genType()
3811class UniqueObjectsOutputGenerator(OutputGenerator):
3812 """Generate UniqueObjects code based on XML element attributes"""
3813 # This is an ordered list of sections in the header file.
3814 ALL_SECTIONS = ['command']
3815 def __init__(self,
3816 errFile = sys.stderr,
3817 warnFile = sys.stderr,
3818 diagFile = sys.stdout):
3819 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
3820 self.INDENT_SPACES = 4
3821 # Commands to ignore
3822 self.intercepts = []
3823 # Commands which are not autogenerated but still intercepted
3824 self.no_autogen_list = [
3825 'vkGetDeviceProcAddr',
3826 'vkGetInstanceProcAddr',
3827 'vkCreateInstance',
3828 'vkDestroyInstance',
3829 'vkCreateDevice',
3830 'vkDestroyDevice',
3831 'vkAllocateMemory',
3832 'vkCreateComputePipelines',
3833 'vkCreateGraphicsPipelines',
3834 'vkCreateSwapchainKHR',
3835 'vkGetSwapchainImagesKHR',
3836 'vkEnumerateInstanceLayerProperties',
3837 'vkEnumerateDeviceLayerProperties',
3838 'vkEnumerateInstanceExtensionProperties',
3839 ]
3840 # Commands shadowed by interface functions and are not implemented
3841 self.interface_functions = [
3842 'vkGetPhysicalDeviceDisplayPropertiesKHR',
3843 'vkGetPhysicalDeviceDisplayPlanePropertiesKHR',
3844 'vkGetDisplayPlaneSupportedDisplaysKHR',
3845 'vkGetDisplayModePropertiesKHR',
3846 # DebugReport APIs are hooked, but handled separately in the source file
3847 'vkCreateDebugReportCallbackEXT',
3848 'vkDestroyDebugReportCallbackEXT',
3849 'vkDebugReportMessageEXT',
3850 ]
3851 self.headerVersion = None
3852 # Internal state - accumulators for different inner block text
3853 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
3854 self.structNames = [] # List of Vulkan struct typenames
3855 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
3856 self.handleTypes = set() # Set of handle type names
3857 self.commands = [] # List of CommandData records for all Vulkan commands
3858 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
3859 self.flags = set() # Map of flags typenames
3860 # Named tuples to store struct and command data
3861 self.StructType = namedtuple('StructType', ['name', 'value'])
3862 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isconst', 'iscount', 'len', 'extstructs', 'cdecl', 'islocal', 'iscreate', 'isdestroy'])
3863 self.CommandData = namedtuple('CommandData', ['name', 'return_type', 'params', 'cdecl'])
3864 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
3865 #
3866 def incIndent(self, indent):
3867 inc = ' ' * self.INDENT_SPACES
3868 if indent:
3869 return indent + inc
3870 return inc
3871 #
3872 def decIndent(self, indent):
3873 if indent and (len(indent) > self.INDENT_SPACES):
3874 return indent[:-self.INDENT_SPACES]
3875 return ''
3876 #
3877 # Override makeProtoName to drop the "vk" prefix
3878 def makeProtoName(self, name, tail):
3879 return self.genOpts.apientry + name[2:] + tail
3880 #
3881 # Check if the parameter passed in is a pointer to an array
3882 def paramIsArray(self, param):
3883 return param.attrib.get('len') is not None
3884 #
3885 def beginFile(self, genOpts):
3886 OutputGenerator.beginFile(self, genOpts)
3887 # User-supplied prefix text, if any (list of strings)
3888 if (genOpts.prefixText):
3889 for s in genOpts.prefixText:
3890 write(s, file=self.outFile)
3891 # Namespace
3892 self.newline()
3893 write('namespace unique_objects {', file = self.outFile)
3894 #
3895 def endFile(self):
3896 self.newline()
3897 # Record intercepted procedures
3898 write('// intercepts', file=self.outFile)
3899 write('struct { const char* name; PFN_vkVoidFunction pFunc;} procmap[] = {', file=self.outFile)
3900 write('\n'.join(self.intercepts), file=self.outFile)
3901 write('};\n', file=self.outFile)
3902 self.newline()
3903 write('} // namespace unique_objects', file=self.outFile)
3904 if (self.genOpts.protectFile and self.genOpts.filename):
3905 self.newline()
3906 write('#endif', file=self.outFile)
3907 # Finish processing in superclass
3908 OutputGenerator.endFile(self)
3909 #
3910 def beginFeature(self, interface, emit):
3911 # Start processing in superclass
3912 OutputGenerator.beginFeature(self, interface, emit)
3913 self.headerVersion = None
3914 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
3915 self.structNames = []
3916 self.structTypes = dict()
3917 self.handleTypes = set()
3918 self.commands = []
3919 self.structMembers = []
3920 self.cmdMembers = []
3921 self.flags = set()
3922 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
3923 self.CmdMemberData = namedtuple('CmdMemberData', ['name', 'members'])
3924 #
3925 def endFeature(self):
3926 # Actually write the interface to the output file.
3927 if (self.emit):
3928 self.newline()
3929 if (self.featureExtraProtect != None):
3930 write('#ifdef', self.featureExtraProtect, file=self.outFile)
3931 # Write the unique_objects code to the file
3932 if (self.sections['command']):
3933 if (self.genOpts.protectProto):
3934 write(self.genOpts.protectProto,
3935 self.genOpts.protectProtoStr, file=self.outFile)
3936 write('\n'.join(self.sections['command']), end='', file=self.outFile)
3937 if (self.featureExtraProtect != None):
3938 write('\n#endif //', self.featureExtraProtect, file=self.outFile)
3939 else:
3940 self.newline()
3941 # Finish processing in superclass
3942 OutputGenerator.endFeature(self)
3943 #
3944 def genType(self, typeinfo, name):
3945 OutputGenerator.genType(self, typeinfo, name)
3946 typeElem = typeinfo.elem
3947 # If the type is a struct type, traverse the imbedded <member> tags generating a structure.
3948 # Otherwise, emit the tag text.
3949 category = typeElem.get('category')
3950 if (category == 'struct' or category == 'union'):
3951 self.structNames.append(name)
3952 self.genStruct(typeinfo, name)
3953 #
3954 # Append a definition to the specified section
3955 def appendSection(self, section, text):
3956 # self.sections[section].append('SECTION: ' + section + '\n')
3957 self.sections[section].append(text)
3958 #
3959 # Check if the parameter passed in is a pointer
3960 def paramIsPointer(self, param):
3961 ispointer = False
3962 for elem in param:
3963 if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
3964 ispointer = True
3965 return ispointer
3966 #
3967 # Get the category of a type
3968 def getTypeCategory(self, typename):
3969 types = self.registry.findall("types/type")
3970 for elem in types:
3971 if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
3972 return elem.attrib.get('category')
3973 #
3974 # Check if a parent object is dispatchable or not
3975 def isHandleTypeNonDispatchable(self, handletype):
3976 handle = self.registry.find("types/type/[name='" + handletype + "'][@category='handle']")
3977 if handle is not None and handle.find('type').text == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE':
3978 return True
3979 else:
3980 return False
3981 #
3982 # Retrieve the type and name for a parameter
3983 def getTypeNameTuple(self, param):
3984 type = ''
3985 name = ''
3986 for elem in param:
3987 if elem.tag == 'type':
3988 type = noneStr(elem.text)
3989 elif elem.tag == 'name':
3990 name = noneStr(elem.text)
3991 return (type, name)
3992 #
3993 # Retrieve the value of the len tag
3994 def getLen(self, param):
3995 result = None
3996 len = param.attrib.get('len')
3997 if len and len != 'null-terminated':
3998 # For string arrays, 'len' can look like 'count,null-terminated', indicating that we
3999 # have a null terminated array of strings. We strip the null-terminated from the
4000 # 'len' field and only return the parameter specifying the string count
4001 if 'null-terminated' in len:
4002 result = len.split(',')[0]
4003 else:
4004 result = len
4005 # Spec has now notation for len attributes, using :: instead of platform specific pointer symbol
4006 result = str(result).replace('::', '->')
4007 return result
4008 #
4009 # Generate a VkStructureType based on a structure typename
4010 def genVkStructureType(self, typename):
4011 # Add underscore between lowercase then uppercase
4012 value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
4013 # Change to uppercase
4014 value = value.upper()
4015 # Add STRUCTURE_TYPE_
4016 return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
4017 #
4018 # Struct parameter check generation.
4019 # This is a special case of the <type> tag where the contents are interpreted as a set of
4020 # <member> tags instead of freeform C type declarations. The <member> tags are just like
4021 # <param> tags - they are a declaration of a struct or union member. Only simple member
4022 # declarations are supported (no nested structs etc.)
4023 def genStruct(self, typeinfo, typeName):
4024 OutputGenerator.genStruct(self, typeinfo, typeName)
4025 members = typeinfo.elem.findall('.//member')
4026 # Iterate over members once to get length parameters for arrays
4027 lens = set()
4028 for member in members:
4029 len = self.getLen(member)
4030 if len:
4031 lens.add(len)
4032 # Generate member info
4033 membersInfo = []
4034 for member in members:
4035 # Get the member's type and name
4036 info = self.getTypeNameTuple(member)
4037 type = info[0]
4038 name = info[1]
4039 cdecl = self.makeCParamDecl(member, 0)
4040 # Process VkStructureType
4041 if type == 'VkStructureType':
4042 # Extract the required struct type value from the comments
4043 # embedded in the original text defining the 'typeinfo' element
4044 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
4045 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
4046 if result:
4047 value = result.group(0)
4048 else:
4049 value = self.genVkStructureType(typeName)
4050 # Store the required type value
4051 self.structTypes[typeName] = self.StructType(name=name, value=value)
4052 # Store pointer/array/string info
4053 membersInfo.append(self.CommandParam(type=type,
4054 name=name,
4055 ispointer=self.paramIsPointer(member),
4056 isconst=True if 'const' in cdecl else False,
4057 iscount=True if name in lens else False,
4058 len=self.getLen(member),
4059 extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None,
4060 cdecl=cdecl,
4061 islocal=False,
4062 iscreate=False,
4063 isdestroy=False))
4064 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
4065 #
4066 # Insert a lock_guard line
4067 def lock_guard(self, indent):
4068 return '%sstd::lock_guard<std::mutex> lock(global_lock);\n' % indent
4069 #
4070 # Determine if a struct has an NDO as a member or an embedded member
4071 def struct_contains_ndo(self, struct_item):
4072 struct_member_dict = dict(self.structMembers)
4073 struct_members = struct_member_dict[struct_item]
4074
4075 for member in struct_members:
4076 if self.isHandleTypeNonDispatchable(member.type):
4077 return True
4078 elif member.type in struct_member_dict:
4079 if self.struct_contains_ndo(member.type) == True:
4080 return True
4081 return False
4082 #
4083 # Return list of struct members which contain, or which sub-structures contain
4084 # an NDO in a given list of parameters or members
4085 def getParmeterStructsWithNdos(self, item_list):
4086 struct_list = set()
4087 for item in item_list:
4088 paramtype = item.find('type')
4089 typecategory = self.getTypeCategory(paramtype.text)
4090 if typecategory == 'struct':
4091 if self.struct_contains_ndo(paramtype.text) == True:
4092 struct_list.add(item)
4093 return struct_list
4094 #
4095 # Return list of non-dispatchable objects from a given list of parameters or members
4096 def getNdosInParameterList(self, item_list, create_func):
4097 ndo_list = set()
4098 if create_func == True:
4099 member_list = item_list[0:-1]
4100 else:
4101 member_list = item_list
4102 for item in member_list:
4103 if self.isHandleTypeNonDispatchable(paramtype.text):
4104 ndo_list.add(item)
4105 return ndo_list
4106 #
4107 # Generate source for creating a non-dispatchable object
4108 def generate_create_ndo_code(self, indent, proto, params, cmd_info):
4109 create_ndo_code = ''
4110 if True in [create_txt in proto.text for create_txt in ['Create', 'Allocate']]:
4111 handle_type = params[-1].find('type')
4112 if self.isHandleTypeNonDispatchable(handle_type.text):
4113 # Check for special case where multiple handles are returned
4114 ndo_array = False
4115 if cmd_info[-1].len is not None:
4116 ndo_array = True;
4117 handle_name = params[-1].find('name')
4118 create_ndo_code += '%sif (VK_SUCCESS == result) {\n' % (indent)
4119 indent = self.incIndent(indent)
4120 create_ndo_code += '%sstd::lock_guard<std::mutex> lock(global_lock);\n' % (indent)
4121 ndo_dest = '*%s' % handle_name.text
4122 if ndo_array == True:
4123 create_ndo_code += '%sfor (uint32_t index0 = 0; index0 < %s; index0++) {\n' % (indent, cmd_info[-1].len)
4124 indent = self.incIndent(indent)
4125 ndo_dest = '%s[index0]' % cmd_info[-1].name
4126 create_ndo_code += '%suint64_t unique_id = global_unique_id++;\n' % (indent)
4127 create_ndo_code += '%sdev_data->unique_id_mapping[unique_id] = reinterpret_cast<uint64_t &>(%s);\n' % (indent, ndo_dest)
4128 create_ndo_code += '%s%s = reinterpret_cast<%s&>(unique_id);\n' % (indent, ndo_dest, handle_type.text)
4129 if ndo_array == True:
4130 indent = self.decIndent(indent)
4131 create_ndo_code += '%s}\n' % indent
4132 indent = self.decIndent(indent)
4133 create_ndo_code += '%s}\n' % (indent)
4134 return create_ndo_code
4135 #
4136 # Generate source for destroying a non-dispatchable object
4137 def generate_destroy_ndo_code(self, indent, proto, cmd_info):
4138 destroy_ndo_code = ''
4139 ndo_array = False
4140 if True in [destroy_txt in proto.text for destroy_txt in ['Destroy', 'Free']]:
4141 # Check for special case where multiple handles are returned
4142 if cmd_info[-1].len is not None:
4143 ndo_array = True;
4144 param = -1
4145 else:
4146 param = -2
4147 if self.isHandleTypeNonDispatchable(cmd_info[param].type) == True:
4148 if ndo_array == True:
4149 # This API is freeing an array of handles. Remove them from the unique_id map.
4150 destroy_ndo_code += '%sif ((VK_SUCCESS == result) && (%s)) {\n' % (indent, cmd_info[param].name)
4151 indent = self.incIndent(indent)
4152 destroy_ndo_code += '%sstd::unique_lock<std::mutex> lock(global_lock);\n' % (indent)
4153 destroy_ndo_code += '%sfor (uint32_t index0 = 0; index0 < %s; index0++) {\n' % (indent, cmd_info[param].len)
4154 indent = self.incIndent(indent)
4155 destroy_ndo_code += '%s%s handle = %s[index0];\n' % (indent, cmd_info[param].type, cmd_info[param].name)
4156 destroy_ndo_code += '%suint64_t unique_id = reinterpret_cast<uint64_t &>(handle);\n' % (indent)
4157 destroy_ndo_code += '%sdev_data->unique_id_mapping.erase(unique_id);\n' % (indent)
4158 indent = self.decIndent(indent);
4159 destroy_ndo_code += '%s}\n' % indent
4160 indent = self.decIndent(indent);
4161 destroy_ndo_code += '%s}\n' % indent
4162 else:
4163 # Remove a single handle from the map
4164 destroy_ndo_code += '%sstd::unique_lock<std::mutex> lock(global_lock);\n' % (indent)
4165 destroy_ndo_code += '%suint64_t %s_id = reinterpret_cast<uint64_t &>(%s);\n' % (indent, cmd_info[param].name, cmd_info[param].name)
4166 destroy_ndo_code += '%s%s = (%s)dev_data->unique_id_mapping[%s_id];\n' % (indent, cmd_info[param].name, cmd_info[param].type, cmd_info[param].name)
4167 destroy_ndo_code += '%sdev_data->unique_id_mapping.erase(%s_id);\n' % (indent, cmd_info[param].name)
4168 destroy_ndo_code += '%slock.unlock();\n' % (indent)
4169 return ndo_array, destroy_ndo_code
4170
4171 #
4172 # Clean up local declarations
4173 def cleanUpLocalDeclarations(self, indent, prefix, name, len):
4174 cleanup = '%sif (local_%s%s)\n' % (indent, prefix, name)
4175 if len is not None:
4176 cleanup += '%s delete[] local_%s%s;\n' % (indent, prefix, name)
4177 else:
4178 cleanup += '%s delete local_%s%s;\n' % (indent, prefix, name)
4179 return cleanup
4180 #
4181 # Output UO code for a single NDO (ndo_count is NULL) or a counted list of NDOs
4182 def outputNDOs(self, ndo_type, ndo_name, ndo_count, prefix, index, indent, destroy_func, destroy_array, top_level):
4183 decl_code = ''
4184 pre_call_code = ''
4185 post_call_code = ''
4186 if ndo_count is not None:
4187 if top_level == True:
4188 decl_code += '%s%s *local_%s%s = NULL;\n' % (indent, ndo_type, prefix, ndo_name)
4189 pre_call_code += '%s if (%s%s) {\n' % (indent, prefix, ndo_name)
4190 indent = self.incIndent(indent)
4191 if top_level == True:
4192 pre_call_code += '%s local_%s%s = new %s[%s];\n' % (indent, prefix, ndo_name, ndo_type, ndo_count)
4193 pre_call_code += '%s for (uint32_t %s = 0; %s < %s; ++%s) {\n' % (indent, index, index, ndo_count, index)
4194 indent = self.incIndent(indent)
4195 pre_call_code += '%s local_%s%s[%s] = (%s)dev_data->unique_id_mapping[reinterpret_cast<const uint64_t &>(%s[%s])];\n' % (indent, prefix, ndo_name, index, ndo_type, ndo_name, index)
4196 else:
4197 pre_call_code += '%s for (uint32_t %s = 0; %s < %s; ++%s) {\n' % (indent, index, index, ndo_count, index)
4198 indent = self.incIndent(indent)
4199 pre_call_code += '%s %s%s[%s] = (%s)dev_data->unique_id_mapping[reinterpret_cast<const uint64_t &>(%s%s[%s])];\n' % (indent, prefix, ndo_name, index, ndo_type, prefix, ndo_name, index)
4200 indent = self.decIndent(indent)
4201 pre_call_code += '%s }\n' % indent
4202 indent = self.decIndent(indent)
4203 pre_call_code += '%s }\n' % indent
4204 if top_level == True:
4205 post_call_code += '%sif (local_%s%s)\n' % (indent, prefix, ndo_name)
4206 indent = self.incIndent(indent)
4207 post_call_code += '%sdelete[] local_%s;\n' % (indent, ndo_name)
4208 else:
4209 if top_level == True:
4210 if (destroy_func == False) or (destroy_array == True): #### LUGMAL This line needs to be skipped for destroy_ndo and not destroy_array
4211 pre_call_code += '%s %s = (%s)dev_data->unique_id_mapping[reinterpret_cast<uint64_t &>(%s)];\n' % (indent, ndo_name, ndo_type, ndo_name)
4212 else:
4213 # Make temp copy of this var with the 'local' removed. It may be better to not pass in 'local_'
4214 # as part of the string and explicitly print it
4215 fix = str(prefix).strip('local_');
4216 pre_call_code += '%s if (%s%s) {\n' % (indent, fix, ndo_name)
4217 indent = self.incIndent(indent)
4218 pre_call_code += '%s %s%s = (%s)dev_data->unique_id_mapping[reinterpret_cast<const uint64_t &>(%s%s)];\n' % (indent, prefix, ndo_name, ndo_type, fix, ndo_name)
4219 indent = self.decIndent(indent)
4220 pre_call_code += '%s }\n' % indent
4221 return decl_code, pre_call_code, post_call_code
4222 #
4223 # first_level_param indicates if elements are passed directly into the function else they're below a ptr/struct
4224 # create_func means that this is API creates or allocates NDOs
4225 # destroy_func indicates that this API destroys or frees NDOs
4226 # destroy_array means that the destroy_func operated on an array of NDOs
4227 def uniquify_members(self, members, indent, prefix, array_index, create_func, destroy_func, destroy_array, first_level_param):
4228 decls = ''
4229 pre_code = ''
4230 post_code = ''
4231 struct_member_dict = dict(self.structMembers)
4232 index = 'index%s' % str(array_index)
4233 array_index += 1
4234 # Process any NDOs in this structure and recurse for any sub-structs in this struct
4235 for member in members:
4236 # Handle NDOs
4237 if self.isHandleTypeNonDispatchable(member.type) == True:
4238 count_name = member.len
4239 if (count_name is not None):
4240 if first_level_param == False:
4241 count_name = '%s%s' % (prefix, member.len)
4242
4243 if (first_level_param == False) or (create_func == False):
4244 (tmp_decl, tmp_pre, tmp_post) = self.outputNDOs(member.type, member.name, count_name, prefix, index, indent, destroy_func, destroy_array, first_level_param)
4245 decls += tmp_decl
4246 pre_code += tmp_pre
4247 post_code += tmp_post
4248 # Handle Structs that contain NDOs at some level
4249 elif member.type in struct_member_dict:
4250 # All structs at first level will have an NDO
4251 if self.struct_contains_ndo(member.type) == True:
4252 struct_info = struct_member_dict[member.type]
4253 # Struct Array
4254 if member.len is not None:
4255 # Update struct prefix
4256 if first_level_param == True:
4257 new_prefix = 'local_%s' % member.name
4258 # Declare safe_VarType for struct
4259 decls += '%ssafe_%s *%s = NULL;\n' % (indent, member.type, new_prefix)
4260 else:
4261 new_prefix = '%s%s' % (prefix, member.name)
4262 pre_code += '%s if (%s%s) {\n' % (indent, prefix, member.name)
4263 indent = self.incIndent(indent)
4264 if first_level_param == True:
4265 pre_code += '%s %s = new safe_%s[%s];\n' % (indent, new_prefix, member.type, member.len)
4266 pre_code += '%s for (uint32_t %s = 0; %s < %s%s; ++%s) {\n' % (indent, index, index, prefix, member.len, index)
4267 indent = self.incIndent(indent)
4268 if first_level_param == True:
4269 pre_code += '%s %s[%s].initialize(&%s[%s]);\n' % (indent, new_prefix, index, member.name, index)
4270 local_prefix = '%s[%s].' % (new_prefix, index)
4271 # Process sub-structs in this struct
4272 (tmp_decl, tmp_pre, tmp_post) = self.uniquify_members(struct_info, indent, local_prefix, array_index, create_func, destroy_func, destroy_array, False)
4273 decls += tmp_decl
4274 pre_code += tmp_pre
4275 post_code += tmp_post
4276 indent = self.decIndent(indent)
4277 pre_code += '%s }\n' % indent
4278 indent = self.decIndent(indent)
4279 pre_code += '%s }\n' % indent
4280 if first_level_param == True:
4281 post_code += self.cleanUpLocalDeclarations(indent, prefix, member.name, member.len)
4282 # Single Struct
4283 else:
4284 # Update struct prefix
4285 if first_level_param == True:
4286 new_prefix = 'local_%s->' % member.name
4287 decls += '%ssafe_%s *local_%s%s = NULL;\n' % (indent, member.type, prefix, member.name)
4288 else:
4289 new_prefix = '%s%s->' % (prefix, member.name)
4290 # Declare safe_VarType for struct
4291 pre_code += '%s if (%s%s) {\n' % (indent, prefix, member.name)
4292 indent = self.incIndent(indent)
4293 if first_level_param == True:
4294 pre_code += '%s local_%s%s = new safe_%s(%s);\n' % (indent, prefix, member.name, member.type, member.name)
4295 # Process sub-structs in this struct
4296 (tmp_decl, tmp_pre, tmp_post) = self.uniquify_members(struct_info, indent, new_prefix, array_index, create_func, destroy_func, destroy_array, False)
4297 decls += tmp_decl
4298 pre_code += tmp_pre
4299 post_code += tmp_post
4300 indent = self.decIndent(indent)
4301 pre_code += '%s }\n' % indent
4302 if first_level_param == True:
4303 post_code += self.cleanUpLocalDeclarations(indent, prefix, member.name, member.len)
4304 return decls, pre_code, post_code
4305 #
4306 # For a particular API, generate the non-dispatchable-object wrapping/unwrapping code
4307 def generate_wrapping_code(self, cmd):
4308 indent = ' '
4309 proto = cmd.find('proto/name')
4310 params = cmd.findall('param')
4311 if proto.text is not None:
4312 cmd_member_dict = dict(self.cmdMembers)
4313 cmd_info = cmd_member_dict[proto.text]
4314 # Handle ndo create/allocate operations
4315 if cmd_info[0].iscreate:
4316 create_ndo_code = self.generate_create_ndo_code(indent, proto, params, cmd_info)
4317 else:
4318 create_ndo_code = ''
4319 # Handle ndo destroy/free operations
4320 if cmd_info[0].isdestroy:
4321 (destroy_array, destroy_ndo_code) = self.generate_destroy_ndo_code(indent, proto, cmd_info)
4322 else:
4323 destroy_array = False
4324 destroy_ndo_code = ''
4325 paramdecl = ''
4326 param_pre_code = ''
4327 param_post_code = ''
4328 create_func = True if create_ndo_code else False
4329 destroy_func = True if destroy_ndo_code else False
4330 (paramdecl, param_pre_code, param_post_code) = self.uniquify_members(cmd_info, indent, '', 0, create_func, destroy_func, destroy_array, True)
4331 param_post_code += create_ndo_code
4332 if destroy_ndo_code:
4333 if destroy_array == True:
4334 param_post_code += destroy_ndo_code
4335 else:
4336 param_pre_code += destroy_ndo_code
4337 if param_pre_code:
4338 if (not destroy_func) or (destroy_array):
4339 param_pre_code = '%s{\n%s%s%s%s}\n' % (' ', indent, self.lock_guard(indent), param_pre_code, indent)
4340 return paramdecl, param_pre_code, param_post_code
4341 #
4342 # Capture command parameter info needed to wrap NDOs as well as handling some boilerplate code
4343 def genCmd(self, cmdinfo, cmdname):
4344 if cmdname in self.interface_functions:
4345 return
4346 if cmdname in self.no_autogen_list:
4347 decls = self.makeCDecls(cmdinfo.elem)
4348 self.appendSection('command', '')
4349 self.appendSection('command', '// Declare only')
4350 self.appendSection('command', decls[0])
4351 self.intercepts += [ ' {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (cmdname,cmdname[2:]) ]
4352 return
4353 # Add struct-member type information to command parameter information
4354 OutputGenerator.genCmd(self, cmdinfo, cmdname)
4355 members = cmdinfo.elem.findall('.//param')
4356 # Iterate over members once to get length parameters for arrays
4357 lens = set()
4358 for member in members:
4359 len = self.getLen(member)
4360 if len:
4361 lens.add(len)
4362 struct_member_dict = dict(self.structMembers)
4363 # Generate member info
4364 membersInfo = []
4365 for member in members:
4366 # Get type and name of member
4367 info = self.getTypeNameTuple(member)
4368 type = info[0]
4369 name = info[1]
4370 cdecl = self.makeCParamDecl(member, 0)
4371 # Check for parameter name in lens set
4372 iscount = True if name in lens else False
4373 len = self.getLen(member)
4374 isconst = True if 'const' in cdecl else False
4375 ispointer = self.paramIsPointer(member)
4376 # Mark param as local if it is an array of NDOs
4377 islocal = False;
4378 if self.isHandleTypeNonDispatchable(type) == True:
4379 if (len is not None) and (isconst == True):
4380 islocal = True
4381 # Or if it's a struct that contains an NDO
4382 elif type in struct_member_dict:
4383 if self.struct_contains_ndo(type) == True:
4384 islocal = True
4385
4386 isdestroy = True if True in [destroy_txt in cmdname for destroy_txt in ['Destroy', 'Free']] else False
4387 iscreate = True if True in [create_txt in cmdname for create_txt in ['Create', 'Allocate']] else False
4388
4389 membersInfo.append(self.CommandParam(type=type,
4390 name=name,
4391 ispointer=ispointer,
4392 isconst=isconst,
4393 iscount=iscount,
4394 len=len,
4395 extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None,
4396 cdecl=cdecl,
4397 islocal=islocal,
4398 iscreate=iscreate,
4399 isdestroy=isdestroy))
4400 self.cmdMembers.append(self.CmdMemberData(name=cmdname, members=membersInfo))
4401 # Generate NDO wrapping/unwrapping code for all parameters
4402 (api_decls, api_pre, api_post) = self.generate_wrapping_code(cmdinfo.elem)
4403 # If API doesn't contain an NDO's, don't fool with it
4404 if not api_decls and not api_pre and not api_post:
4405 return
4406 # Record that the function will be intercepted
4407 if (self.featureExtraProtect != None):
4408 self.intercepts += [ '#ifdef %s' % self.featureExtraProtect ]
4409 self.intercepts += [ ' {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (cmdname,cmdname[2:]) ]
4410 if (self.featureExtraProtect != None):
4411 self.intercepts += [ '#endif' ]
4412 decls = self.makeCDecls(cmdinfo.elem)
4413 self.appendSection('command', '')
4414 self.appendSection('command', decls[0][:-1])
4415 self.appendSection('command', '{')
4416 # Setup common to call wrappers, first parameter is always dispatchable
4417 dispatchable_type = cmdinfo.elem.find('param/type').text
4418 dispatchable_name = cmdinfo.elem.find('param/name').text
4419 # Generate local instance/pdev/device data lookup
4420 self.appendSection('command', ' layer_data *dev_data = get_my_data_ptr(get_dispatch_key('+dispatchable_name+'), layer_data_map);')
4421 # Handle return values, if any
4422 resulttype = cmdinfo.elem.find('proto/type')
4423 if (resulttype != None and resulttype.text == 'void'):
4424 resulttype = None
4425 if (resulttype != None):
4426 assignresult = resulttype.text + ' result = '
4427 else:
4428 assignresult = ''
4429 # Pre-pend declarations and pre-api-call codegen
4430 if api_decls:
4431 self.appendSection('command', "\n".join(str(api_decls).rstrip().split("\n")))
4432 if api_pre:
4433 self.appendSection('command', "\n".join(str(api_pre).rstrip().split("\n")))
4434 # Generate the API call itself
4435 # Gather the parameter items
4436 params = cmdinfo.elem.findall('param/name')
4437 # Pull out the text for each of the parameters, separate them by commas in a list
4438 paramstext = ', '.join([str(param.text) for param in params])
4439 # If any of these paramters has been replaced by a local var, fix up the list
4440 cmd_member_dict = dict(self.cmdMembers)
4441 params = cmd_member_dict[cmdname]
4442 for param in params:
4443 if param.islocal == True:
4444 if param.ispointer == True:
4445 paramstext = paramstext.replace(param.name, '(%s %s*)local_%s' % ('const', param.type, param.name))
4446 else:
4447 paramstext = paramstext.replace(param.name, '(%s %s)local_%s' % ('const', param.type, param.name))
4448 # Use correct dispatch table
4449 if dispatchable_type in ["VkPhysicalDevice", "VkInstance"]:
4450 API = cmdinfo.elem.attrib.get('name').replace('vk','dev_data->instance_dispatch_table->',1)
4451 else:
4452 API = cmdinfo.elem.attrib.get('name').replace('vk','dev_data->device_dispatch_table->',1)
4453 # Put all this together for the final down-chain call
4454 self.appendSection('command', ' ' + assignresult + API + '(' + paramstext + ');')
4455 # And add the post-API-call codegen
4456 self.appendSection('command', "\n".join(str(api_post).rstrip().split("\n")))
4457 # Handle the return result variable, if any
4458 if (resulttype != None):
4459 self.appendSection('command', ' return result;')
4460 self.appendSection('command', '}')