layers: bring xml and scripts from documentation
These are repo git@gitlab.khronos.org:vulkan/vulkan.git
from commit 51ffe091889e08ca5196c5635363a5358c85ff82
diff --git a/generator.py b/generator.py
new file mode 100644
index 0000000..b572986
--- /dev/null
+++ b/generator.py
@@ -0,0 +1,2184 @@
+#!/usr/bin/python3 -i
+import os,re,sys
+
+def write( *args, **kwargs ):
+ file = kwargs.pop('file',sys.stdout)
+ end = kwargs.pop( 'end','\n')
+ file.write( ' '.join([str(arg) for arg in args]) )
+ file.write( end )
+
+# noneStr - returns string argument, or "" if argument is None.
+# Used in converting lxml Elements into text.
+# str - string to convert
+def noneStr(str):
+ if (str):
+ return str
+ else:
+ return ""
+
+# enquote - returns string argument with surrounding quotes,
+# for serialization into Python code.
+def enquote(str):
+ if (str):
+ return "'" + str + "'"
+ else:
+ return None
+
+# Primary sort key for regSortFeatures.
+# Sorts by category of the feature name string:
+# Core API features (those defined with a <feature> tag)
+# ARB/KHR/OES (Khronos extensions)
+# other (EXT/vendor extensions)
+# This will need changing for Vulkan!
+def regSortCategoryKey(feature):
+ if (feature.elem.tag == 'feature'):
+ return 0
+ elif (feature.category == 'ARB' or
+ feature.category == 'KHR' or
+ feature.category == 'OES'):
+ return 1
+ else:
+ return 2
+
+# Secondary sort key for regSortFeatures.
+# Sorts by extension name.
+def regSortNameKey(feature):
+ return feature.name
+
+# Second sort key for regSortFeatures.
+# Sorts by feature version. <extension> elements all have version number "0"
+def regSortFeatureVersionKey(feature):
+ return float(feature.version)
+
+# Tertiary sort key for regSortFeatures.
+# Sorts by extension number. <feature> elements all have extension number 0.
+def regSortExtensionNumberKey(feature):
+ return int(feature.number)
+
+# regSortFeatures - default sort procedure for features.
+# Sorts by primary key of feature category ('feature' or 'extension')
+# then by version number (for features)
+# then by extension number (for extensions)
+def regSortFeatures(featureList):
+ featureList.sort(key = regSortExtensionNumberKey)
+ featureList.sort(key = regSortFeatureVersionKey)
+ featureList.sort(key = regSortCategoryKey)
+
+# GeneratorOptions - base class for options used during header production
+# These options are target language independent, and used by
+# Registry.apiGen() and by base OutputGenerator objects.
+#
+# Members
+# filename - name of file to generate, or None to write to stdout.
+# apiname - string matching <api> 'apiname' attribute, e.g. 'gl'.
+# profile - string specifying API profile , e.g. 'core', or None.
+# versions - regex matching API versions to process interfaces for.
+# Normally '.*' or '[0-9]\.[0-9]' to match all defined versions.
+# emitversions - regex matching API versions to actually emit
+# interfaces for (though all requested versions are considered
+# when deciding which interfaces to generate). For GL 4.3 glext.h,
+# this might be '1\.[2-5]|[2-4]\.[0-9]'.
+# defaultExtensions - If not None, a string which must in its
+# entirety match the pattern in the "supported" attribute of
+# the <extension>. Defaults to None. Usually the same as apiname.
+# addExtensions - regex matching names of additional extensions
+# to include. Defaults to None.
+# removeExtensions - regex matching names of extensions to
+# remove (after defaultExtensions and addExtensions). Defaults
+# to None.
+# sortProcedure - takes a list of FeatureInfo objects and sorts
+# them in place to a preferred order in the generated output.
+# Default is core API versions, ARB/KHR/OES extensions, all
+# other extensions, alphabetically within each group.
+# The regex patterns can be None or empty, in which case they match
+# nothing.
+class GeneratorOptions:
+ """Represents options during header production from an API registry"""
+ def __init__(self,
+ filename = None,
+ apiname = None,
+ profile = None,
+ versions = '.*',
+ emitversions = '.*',
+ defaultExtensions = None,
+ addExtensions = None,
+ removeExtensions = None,
+ sortProcedure = regSortFeatures):
+ self.filename = filename
+ self.apiname = apiname
+ self.profile = profile
+ self.versions = self.emptyRegex(versions)
+ self.emitversions = self.emptyRegex(emitversions)
+ self.defaultExtensions = defaultExtensions
+ self.addExtensions = self.emptyRegex(addExtensions)
+ self.removeExtensions = self.emptyRegex(removeExtensions)
+ self.sortProcedure = sortProcedure
+ #
+ # Substitute a regular expression which matches no version
+ # or extension names for None or the empty string.
+ def emptyRegex(self,pat):
+ if (pat == None or pat == ''):
+ return '_nomatch_^'
+ else:
+ return pat
+
+# CGeneratorOptions - subclass of GeneratorOptions.
+#
+# Adds options used by COutputGenerator objects during C language header
+# generation.
+#
+# Additional members
+# prefixText - list of strings to prefix generated header with
+# (usually a copyright statement + calling convention macros).
+# protectFile - True if multiple inclusion protection should be
+# generated (based on the filename) around the entire header.
+# protectFeature - True if #ifndef..#endif protection should be
+# generated around a feature interface in the header file.
+# genFuncPointers - True if function pointer typedefs should be
+# generated
+# protectProto - If conditional protection should be generated
+# around prototype declarations, set to either '#ifdef'
+# to require opt-in (#ifdef protectProtoStr) or '#ifndef'
+# to require opt-out (#ifndef protectProtoStr). Otherwise
+# set to None.
+# protectProtoStr - #ifdef/#ifndef symbol to use around prototype
+# declarations, if protectProto is set
+# apicall - string to use for the function declaration prefix,
+# such as APICALL on Windows.
+# apientry - string to use for the calling convention macro,
+# in typedefs, such as APIENTRY.
+# apientryp - string to use for the calling convention macro
+# in function pointer typedefs, such as APIENTRYP.
+# indentFuncProto - True if prototype declarations should put each
+# parameter on a separate line
+# indentFuncPointer - True if typedefed function pointers should put each
+# parameter on a separate line
+# alignFuncParam - if nonzero and parameters are being put on a
+# separate line, align parameter names at the specified column
+class CGeneratorOptions(GeneratorOptions):
+ """Represents options during C interface generation for headers"""
+ def __init__(self,
+ filename = None,
+ apiname = None,
+ profile = None,
+ versions = '.*',
+ emitversions = '.*',
+ defaultExtensions = None,
+ addExtensions = None,
+ removeExtensions = None,
+ sortProcedure = regSortFeatures,
+ prefixText = "",
+ genFuncPointers = True,
+ protectFile = True,
+ protectFeature = True,
+ protectProto = None,
+ protectProtoStr = None,
+ apicall = '',
+ apientry = '',
+ apientryp = '',
+ indentFuncProto = True,
+ indentFuncPointer = False,
+ alignFuncParam = 0):
+ GeneratorOptions.__init__(self, filename, apiname, profile,
+ versions, emitversions, defaultExtensions,
+ addExtensions, removeExtensions, sortProcedure)
+ self.prefixText = prefixText
+ self.genFuncPointers = genFuncPointers
+ self.protectFile = protectFile
+ self.protectFeature = protectFeature
+ self.protectProto = protectProto
+ self.protectProtoStr = protectProtoStr
+ self.apicall = apicall
+ self.apientry = apientry
+ self.apientryp = apientryp
+ self.indentFuncProto = indentFuncProto
+ self.indentFuncPointer = indentFuncPointer
+ self.alignFuncParam = alignFuncParam
+
+# DocGeneratorOptions - subclass of GeneratorOptions.
+#
+# Shares many members with CGeneratorOptions, since
+# both are writing C-style declarations:
+#
+# prefixText - list of strings to prefix generated header with
+# (usually a copyright statement + calling convention macros).
+# apicall - string to use for the function declaration prefix,
+# such as APICALL on Windows.
+# apientry - string to use for the calling convention macro,
+# in typedefs, such as APIENTRY.
+# apientryp - string to use for the calling convention macro
+# in function pointer typedefs, such as APIENTRYP.
+# genDirectory - directory into which to generate include files
+# indentFuncProto - True if prototype declarations should put each
+# parameter on a separate line
+# indentFuncPointer - True if typedefed function pointers should put each
+# parameter on a separate line
+# alignFuncParam - if nonzero and parameters are being put on a
+# separate line, align parameter names at the specified column
+#
+# Additional members:
+#
+class DocGeneratorOptions(GeneratorOptions):
+ """Represents options during C interface generation for Asciidoc"""
+ def __init__(self,
+ filename = None,
+ apiname = None,
+ profile = None,
+ versions = '.*',
+ emitversions = '.*',
+ defaultExtensions = None,
+ addExtensions = None,
+ removeExtensions = None,
+ sortProcedure = regSortFeatures,
+ prefixText = "",
+ apicall = '',
+ apientry = '',
+ apientryp = '',
+ genDirectory = 'gen',
+ indentFuncProto = True,
+ indentFuncPointer = False,
+ alignFuncParam = 0,
+ expandEnumerants = True):
+ GeneratorOptions.__init__(self, filename, apiname, profile,
+ versions, emitversions, defaultExtensions,
+ addExtensions, removeExtensions, sortProcedure)
+ self.prefixText = prefixText
+ self.apicall = apicall
+ self.apientry = apientry
+ self.apientryp = apientryp
+ self.genDirectory = genDirectory
+ self.indentFuncProto = indentFuncProto
+ self.indentFuncPointer = indentFuncPointer
+ self.alignFuncParam = alignFuncParam
+ self.expandEnumerants = expandEnumerants
+
+# OutputGenerator - base class for generating API interfaces.
+# Manages basic logic, logging, and output file control
+# Derived classes actually generate formatted output.
+#
+# ---- methods ----
+# OutputGenerator(errFile, warnFile, diagFile)
+# errFile, warnFile, diagFile - file handles to write errors,
+# warnings, diagnostics to. May be None to not write.
+# logMsg(level, *args) - log messages of different categories
+# level - 'error', 'warn', or 'diag'. 'error' will also
+# raise a UserWarning exception
+# *args - print()-style arguments
+# setExtMap(map) - specify a dictionary map from extension names to
+# numbers, used in creating values for extension enumerants.
+# beginFile(genOpts) - start a new interface file
+# genOpts - GeneratorOptions controlling what's generated and how
+# endFile() - finish an interface file, closing it when done
+# beginFeature(interface, emit) - write interface for a feature
+# and tag generated features as having been done.
+# interface - element for the <version> / <extension> to generate
+# emit - actually write to the header only when True
+# endFeature() - finish an interface.
+# genType(typeinfo,name) - generate interface for a type
+# typeinfo - TypeInfo for a type
+# genStruct(typeinfo,name) - generate interface for a C "struct" type.
+# typeinfo - TypeInfo for a type interpreted as a struct
+# genGroup(groupinfo,name) - generate interface for a group of enums (C "enum")
+# groupinfo - GroupInfo for a group
+# genEnum(enuminfo, name) - generate interface for an enum (constant)
+# enuminfo - EnumInfo for an enum
+# name - enum name
+# genCmd(cmdinfo) - generate interface for a command
+# cmdinfo - CmdInfo for a command
+# makeCDecls(cmd) - return C prototype and function pointer typedef for a
+# <command> Element, as a list of two strings
+# cmd - Element for the <command>
+# newline() - print a newline to the output file (utility function)
+#
+class OutputGenerator:
+ """Generate specified API interfaces in a specific style, such as a C header"""
+ def __init__(self,
+ errFile = sys.stderr,
+ warnFile = sys.stderr,
+ diagFile = sys.stdout):
+ self.outFile = None
+ self.errFile = errFile
+ self.warnFile = warnFile
+ self.diagFile = diagFile
+ # Internal state
+ self.featureName = None
+ self.genOpts = None
+ self.registry = None
+ # Used for extension enum value generation
+ self.extBase = 1000000000
+ self.extBlockSize = 1000
+ #
+ # logMsg - write a message of different categories to different
+ # destinations.
+ # level -
+ # 'diag' (diagnostic, voluminous)
+ # 'warn' (warning)
+ # 'error' (fatal error - raises exception after logging)
+ # *args - print()-style arguments to direct to corresponding log
+ def logMsg(self, level, *args):
+ """Log a message at the given level. Can be ignored or log to a file"""
+ if (level == 'error'):
+ strfile = io.StringIO()
+ write('ERROR:', *args, file=strfile)
+ if (self.errFile != None):
+ write(strfile.getvalue(), file=self.errFile)
+ raise UserWarning(strfile.getvalue())
+ elif (level == 'warn'):
+ if (self.warnFile != None):
+ write('WARNING:', *args, file=self.warnFile)
+ elif (level == 'diag'):
+ if (self.diagFile != None):
+ write('DIAG:', *args, file=self.diagFile)
+ else:
+ raise UserWarning(
+ '*** FATAL ERROR in Generator.logMsg: unknown level:' + level)
+ #
+ # enumToValue - parses and converts an <enum> tag into a value.
+ # Returns a list
+ # first element - integer representation of the value, or None
+ # if needsNum is False. The value must be a legal number
+ # if needsNum is True.
+ # second element - string representation of the value
+ # There are several possible representations of values.
+ # A 'value' attribute simply contains the value.
+ # A 'bitpos' attribute defines a value by specifying the bit
+ # position which is set in that value.
+ # A 'offset','extbase','extends' triplet specifies a value
+ # as an offset to a base value defined by the specified
+ # 'extbase' extension name, which is then cast to the
+ # typename specified by 'extends'. This requires probing
+ # the registry database, and imbeds knowledge of the
+ # Vulkan extension enum scheme in this function.
+ def enumToValue(self, elem, needsNum):
+ name = elem.get('name')
+ numVal = None
+ if ('value' in elem.keys()):
+ value = elem.get('value')
+ # print('About to translate value =', value, 'type =', type(value))
+ if (needsNum):
+ numVal = int(value, 0)
+ # If there's a non-integer, numeric 'type' attribute (e.g. 'u' or
+ # 'ull'), append it to the string value.
+ # t = enuminfo.elem.get('type')
+ # if (t != None and t != '' and t != 'i' and t != 's'):
+ # value += enuminfo.type
+ self.logMsg('diag', 'Enum', name, '-> value [', numVal, ',', value, ']')
+ return [numVal, value]
+ if ('bitpos' in elem.keys()):
+ value = elem.get('bitpos')
+ numVal = int(value, 0)
+ numVal = 1 << numVal
+ value = '0x%08x' % numVal
+ self.logMsg('diag', 'Enum', name, '-> bitpos [', numVal, ',', value, ']')
+ return [numVal, value]
+ if ('offset' in elem.keys()):
+ # Obtain values in the mapping from the attributes
+ enumNegative = False
+ offset = int(elem.get('offset'),0)
+ extnumber = int(elem.get('extnumber'),0)
+ extends = elem.get('extends')
+ if ('dir' in elem.keys()):
+ enumNegative = True
+ self.logMsg('diag', 'Enum', name, 'offset =', offset,
+ 'extnumber =', extnumber, 'extends =', extends,
+ 'enumNegative =', enumNegative)
+ # Now determine the actual enumerant value, as defined
+ # in the "Layers and Extensions" appendix of the spec.
+ numVal = self.extBase + (extnumber - 1) * self.extBlockSize + offset
+ if (enumNegative):
+ numVal = -numVal
+ value = '%d' % numVal
+ # More logic needed!
+ self.logMsg('diag', 'Enum', name, '-> offset [', numVal, ',', value, ']')
+ return [numVal, value]
+ return [None, None]
+ #
+ def beginFile(self, genOpts):
+ self.genOpts = genOpts
+ #
+ # Open specified output file. Not done in constructor since a
+ # Generator can be used without writing to a file.
+ if (self.genOpts.filename != None):
+ self.outFile = open(self.genOpts.filename, 'w')
+ else:
+ self.outFile = sys.stdout
+ def endFile(self):
+ self.errFile and self.errFile.flush()
+ self.warnFile and self.warnFile.flush()
+ self.diagFile and self.diagFile.flush()
+ self.outFile.flush()
+ if (self.outFile != sys.stdout and self.outFile != sys.stderr):
+ self.outFile.close()
+ self.genOpts = None
+ #
+ def beginFeature(self, interface, emit):
+ self.emit = emit
+ self.featureName = interface.get('name')
+ # If there's an additional 'protect' attribute in the feature, save it
+ self.featureExtraProtect = interface.get('protect')
+ def endFeature(self):
+ # Derived classes responsible for emitting feature
+ self.featureName = None
+ self.featureExtraProtect = None
+ # Utility method to validate we're generating something only inside a
+ # <feature> tag
+ def validateFeature(self, featureType, featureName):
+ if (self.featureName == None):
+ raise UserWarning('Attempt to generate', featureType, name,
+ 'when not in feature')
+ #
+ # Type generation
+ def genType(self, typeinfo, name):
+ self.validateFeature('type', name)
+ #
+ # Struct (e.g. C "struct" type) generation
+ def genStruct(self, typeinfo, name):
+ self.validateFeature('struct', name)
+ #
+ # Group (e.g. C "enum" type) generation
+ def genGroup(self, groupinfo, name):
+ self.validateFeature('group', name)
+ #
+ # Enumerant (really, constant) generation
+ def genEnum(self, enuminfo, name):
+ self.validateFeature('enum', name)
+ #
+ # Command generation
+ def genCmd(self, cmd, name):
+ self.validateFeature('command', name)
+ #
+ # Utility functions - turn a <proto> <name> into C-language prototype
+ # and typedef declarations for that name.
+ # name - contents of <name> tag
+ # tail - whatever text follows that tag in the Element
+ def makeProtoName(self, name, tail):
+ return self.genOpts.apientry + name + tail
+ def makeTypedefName(self, name, tail):
+ return '(' + self.genOpts.apientryp + 'PFN_' + name + tail + ')'
+ #
+ # makeCParamDecl - return a string which is an indented, formatted
+ # declaration for a <param> or <member> block (e.g. function parameter
+ # or structure/union member).
+ # param - Element (<param> or <member>) to format
+ # aligncol - if non-zero, attempt to align the nested <name> element
+ # at this column
+ def makeCParamDecl(self, param, aligncol):
+ paramdecl = ' ' + noneStr(param.text)
+ for elem in param:
+ text = noneStr(elem.text)
+ tail = noneStr(elem.tail)
+ if (elem.tag == 'name' and aligncol > 0):
+ self.logMsg('diag', 'Aligning parameter', elem.text, 'to column', self.genOpts.alignFuncParam)
+ # Align at specified column, if possible
+ paramdecl = paramdecl.rstrip()
+ oldLen = len(paramdecl)
+ paramdecl = paramdecl.ljust(aligncol)
+ newLen = len(paramdecl)
+ self.logMsg('diag', 'Adjust length of parameter decl from', oldLen, 'to', newLen, ':', paramdecl)
+ paramdecl += text + tail
+ return paramdecl
+ #
+ # getCParamTypeLength - return the length of the type field is an indented, formatted
+ # declaration for a <param> or <member> block (e.g. function parameter
+ # or structure/union member).
+ # param - Element (<param> or <member>) to identify
+ def getCParamTypeLength(self, param):
+ paramdecl = ' ' + noneStr(param.text)
+ for elem in param:
+ text = noneStr(elem.text)
+ tail = noneStr(elem.tail)
+ if (elem.tag == 'name'):
+ # Align at specified column, if possible
+ newLen = len(paramdecl.rstrip())
+ self.logMsg('diag', 'Identifying length of', elem.text, 'as', newLen)
+ paramdecl += text + tail
+ return newLen
+ #
+ # makeCDecls - return C prototype and function pointer typedef for a
+ # command, as a two-element list of strings.
+ # cmd - Element containing a <command> tag
+ def makeCDecls(self, cmd):
+ """Generate C function pointer typedef for <command> Element"""
+ proto = cmd.find('proto')
+ params = cmd.findall('param')
+ # Begin accumulating prototype and typedef strings
+ pdecl = self.genOpts.apicall
+ tdecl = 'typedef '
+ #
+ # Insert the function return type/name.
+ # For prototypes, add APIENTRY macro before the name
+ # For typedefs, add (APIENTRY *<name>) around the name and
+ # use the PFN_cmdnameproc naming convention.
+ # Done by walking the tree for <proto> element by element.
+ # lxml.etree has elem.text followed by (elem[i], elem[i].tail)
+ # for each child element and any following text
+ # Leading text
+ pdecl += noneStr(proto.text)
+ tdecl += noneStr(proto.text)
+ # For each child element, if it's a <name> wrap in appropriate
+ # declaration. Otherwise append its contents and tail contents.
+ for elem in proto:
+ text = noneStr(elem.text)
+ tail = noneStr(elem.tail)
+ if (elem.tag == 'name'):
+ pdecl += self.makeProtoName(text, tail)
+ tdecl += self.makeTypedefName(text, tail)
+ else:
+ pdecl += text + tail
+ tdecl += text + tail
+ # Now add the parameter declaration list, which is identical
+ # for prototypes and typedefs. Concatenate all the text from
+ # a <param> node without the tags. No tree walking required
+ # since all tags are ignored.
+ # Uses: self.indentFuncProto
+ # self.indentFuncPointer
+ # self.alignFuncParam
+ # Might be able to doubly-nest the joins, e.g.
+ # ','.join(('_'.join([l[i] for i in range(0,len(l))])
+ n = len(params)
+ # Indented parameters
+ if n > 0:
+ indentdecl = '(\n'
+ for i in range(0,n):
+ paramdecl = self.makeCParamDecl(params[i], self.genOpts.alignFuncParam)
+ if (i < n - 1):
+ paramdecl += ',\n'
+ else:
+ paramdecl += ');'
+ indentdecl += paramdecl
+ else:
+ indentdecl = '(void);'
+ # Non-indented parameters
+ paramdecl = '('
+ if n > 0:
+ for i in range(0,n):
+ paramdecl += ''.join([t for t in params[i].itertext()])
+ if (i < n - 1):
+ paramdecl += ', '
+ else:
+ paramdecl += 'void'
+ paramdecl += ");";
+ return [ pdecl + indentdecl, tdecl + paramdecl ]
+ #
+ def newline(self):
+ write('', file=self.outFile)
+
+ def setRegistry(self, registry):
+ self.registry = registry
+ #
+
+# COutputGenerator - subclass of OutputGenerator.
+# Generates C-language API interfaces.
+#
+# ---- methods ----
+# COutputGenerator(errFile, warnFile, diagFile) - args as for
+# OutputGenerator. Defines additional internal state.
+# ---- methods overriding base class ----
+# beginFile(genOpts)
+# endFile()
+# beginFeature(interface, emit)
+# endFeature()
+# genType(typeinfo,name)
+# genStruct(typeinfo,name)
+# genGroup(groupinfo,name)
+# genEnum(enuminfo, name)
+# genCmd(cmdinfo)
+class COutputGenerator(OutputGenerator):
+ """Generate specified API interfaces in a specific style, such as a C header"""
+ # This is an ordered list of sections in the header file.
+ TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
+ 'group', 'bitmask', 'funcpointer', 'struct']
+ ALL_SECTIONS = TYPE_SECTIONS + ['commandPointer', 'command']
+ def __init__(self,
+ errFile = sys.stderr,
+ warnFile = sys.stderr,
+ diagFile = sys.stdout):
+ OutputGenerator.__init__(self, errFile, warnFile, diagFile)
+ # Internal state - accumulators for different inner block text
+ self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
+ #
+ def beginFile(self, genOpts):
+ OutputGenerator.beginFile(self, genOpts)
+ # C-specific
+ #
+ # Multiple inclusion protection & C++ wrappers.
+ if (genOpts.protectFile and self.genOpts.filename):
+ headerSym = '__' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename))
+ write('#ifndef', headerSym, file=self.outFile)
+ write('#define', headerSym, '1', file=self.outFile)
+ self.newline()
+ write('#ifdef __cplusplus', file=self.outFile)
+ write('extern "C" {', file=self.outFile)
+ write('#endif', file=self.outFile)
+ self.newline()
+ #
+ # User-supplied prefix text, if any (list of strings)
+ if (genOpts.prefixText):
+ for s in genOpts.prefixText:
+ write(s, file=self.outFile)
+ #
+ # Some boilerplate describing what was generated - this
+ # will probably be removed later since the extensions
+ # pattern may be very long.
+ # write('/* Generated C header for:', file=self.outFile)
+ # write(' * API:', genOpts.apiname, file=self.outFile)
+ # if (genOpts.profile):
+ # write(' * Profile:', genOpts.profile, file=self.outFile)
+ # write(' * Versions considered:', genOpts.versions, file=self.outFile)
+ # write(' * Versions emitted:', genOpts.emitversions, file=self.outFile)
+ # write(' * Default extensions included:', genOpts.defaultExtensions, file=self.outFile)
+ # write(' * Additional extensions included:', genOpts.addExtensions, file=self.outFile)
+ # write(' * Extensions removed:', genOpts.removeExtensions, file=self.outFile)
+ # write(' */', file=self.outFile)
+ def endFile(self):
+ # C-specific
+ # Finish C++ wrapper and multiple inclusion protection
+ self.newline()
+ write('#ifdef __cplusplus', file=self.outFile)
+ write('}', file=self.outFile)
+ write('#endif', file=self.outFile)
+ if (self.genOpts.protectFile and self.genOpts.filename):
+ self.newline()
+ write('#endif', file=self.outFile)
+ # Finish processing in superclass
+ OutputGenerator.endFile(self)
+ def beginFeature(self, interface, emit):
+ # Start processing in superclass
+ OutputGenerator.beginFeature(self, interface, emit)
+ # C-specific
+ # Accumulate includes, defines, types, enums, function pointer typedefs,
+ # end function prototypes separately for this feature. They're only
+ # printed in endFeature().
+ self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
+ def endFeature(self):
+ # C-specific
+ # Actually write the interface to the output file.
+ if (self.emit):
+ self.newline()
+ if (self.genOpts.protectFeature):
+ write('#ifndef', self.featureName, file=self.outFile)
+ # If type declarations are needed by other features based on
+ # this one, it may be necessary to suppress the ExtraProtect,
+ # or move it below the 'for section...' loop.
+ if (self.featureExtraProtect != None):
+ write('#ifdef', self.featureExtraProtect, file=self.outFile)
+ write('#define', self.featureName, '1', file=self.outFile)
+ for section in self.TYPE_SECTIONS:
+ contents = self.sections[section]
+ if contents:
+ write('\n'.join(contents), file=self.outFile)
+ self.newline()
+ if (self.genOpts.genFuncPointers and self.sections['commandPointer']):
+ write('\n'.join(self.sections['commandPointer']), file=self.outFile)
+ self.newline()
+ if (self.sections['command']):
+ if (self.genOpts.protectProto):
+ write(self.genOpts.protectProto,
+ self.genOpts.protectProtoStr, file=self.outFile)
+ write('\n'.join(self.sections['command']), end='', file=self.outFile)
+ if (self.genOpts.protectProto):
+ write('#endif', file=self.outFile)
+ else:
+ self.newline()
+ if (self.featureExtraProtect != None):
+ write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
+ if (self.genOpts.protectFeature):
+ write('#endif /*', self.featureName, '*/', file=self.outFile)
+ # Finish processing in superclass
+ OutputGenerator.endFeature(self)
+ #
+ # Append a definition to the specified section
+ def appendSection(self, section, text):
+ # self.sections[section].append('SECTION: ' + section + '\n')
+ self.sections[section].append(text)
+ #
+ # Type generation
+ def genType(self, typeinfo, name):
+ OutputGenerator.genType(self, typeinfo, name)
+ typeElem = typeinfo.elem
+ # If the type is a struct type, traverse the imbedded <member> tags
+ # generating a structure. Otherwise, emit the tag text.
+ category = typeElem.get('category')
+ if (category == 'struct' or category == 'union'):
+ self.genStruct(typeinfo, name)
+ else:
+ # Replace <apientry /> tags with an APIENTRY-style string
+ # (from self.genOpts). Copy other text through unchanged.
+ # If the resulting text is an empty string, don't emit it.
+ s = noneStr(typeElem.text)
+ for elem in typeElem:
+ if (elem.tag == 'apientry'):
+ s += self.genOpts.apientry + noneStr(elem.tail)
+ else:
+ s += noneStr(elem.text) + noneStr(elem.tail)
+ if s:
+ # Add extra newline after multi-line entries.
+ if '\n' in s:
+ s += '\n'
+ self.appendSection(category, s)
+ #
+ # Struct (e.g. C "struct" type) generation.
+ # This is a special case of the <type> tag where the contents are
+ # interpreted as a set of <member> tags instead of freeform C
+ # C type declarations. The <member> tags are just like <param>
+ # tags - they are a declaration of a struct or union member.
+ # Only simple member declarations are supported (no nested
+ # structs etc.)
+ def genStruct(self, typeinfo, typeName):
+ OutputGenerator.genStruct(self, typeinfo, typeName)
+ body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
+ # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
+ targetLen = 0;
+ for member in typeinfo.elem.findall('.//member'):
+ targetLen = max(targetLen, self.getCParamTypeLength(member))
+ for member in typeinfo.elem.findall('.//member'):
+ body += self.makeCParamDecl(member, targetLen + 4)
+ body += ';\n'
+ body += '} ' + typeName + ';\n'
+ self.appendSection('struct', body)
+ #
+ # Group (e.g. C "enum" type) generation.
+ # These are concatenated together with other types.
+ def genGroup(self, groupinfo, groupName):
+ OutputGenerator.genGroup(self, groupinfo, groupName)
+ groupElem = groupinfo.elem
+ # See if this group needs min/max/num/padding at end
+ expand = 'expand' in groupElem.keys()
+ if (expand):
+ expandPrefix = groupElem.get('expand')
+ # Prefix
+ body = "\ntypedef enum " + groupName + " {\n"
+
+ # Loop over the nested 'enum' tags. Keep track of the minimum and
+ # maximum numeric values, if they can be determined; but only for
+ # core API enumerants, not extension enumerants. This is inferred
+ # by looking for 'extends' attributes.
+ minName = None
+ for elem in groupElem.findall('enum'):
+ # Convert the value to an integer and use that to track min/max.
+ # Values of form -(number) are accepted but nothing more complex.
+ # Should catch exceptions here for more complex constructs. Not yet.
+ (numVal,strVal) = self.enumToValue(elem, True)
+ name = elem.get('name')
+ body += " " + name + " = " + strVal + ",\n"
+ if (expand and elem.get('extends') is None):
+ if (minName == None):
+ minName = maxName = name
+ minValue = maxValue = numVal
+ elif (numVal < minValue):
+ minName = name
+ minValue = numVal
+ elif (numVal > maxValue):
+ maxName = name
+ maxValue = numVal
+ # Generate min/max value tokens and a range-padding enum. Need some
+ # additional padding to generate correct names...
+ if (expand):
+ body += " " + expandPrefix + "_BEGIN_RANGE = " + minName + ",\n"
+ body += " " + expandPrefix + "_END_RANGE = " + maxName + ",\n"
+ body += " " + expandPrefix + "_RANGE_SIZE = (" + maxName + " - " + minName + " + 1),\n"
+ body += " " + expandPrefix + "_MAX_ENUM = 0x7FFFFFFF\n"
+ # Postfix
+ body += "} " + groupName + ";"
+ if groupElem.get('type') == 'bitmask':
+ section = 'bitmask'
+ else:
+ section = 'group'
+ self.appendSection(section, body)
+ # Enumerant generation
+ # <enum> tags may specify their values in several ways, but are usually
+ # just integers.
+ def genEnum(self, enuminfo, name):
+ OutputGenerator.genEnum(self, enuminfo, name)
+ (numVal,strVal) = self.enumToValue(enuminfo.elem, False)
+ body = '#define ' + name.ljust(33) + ' ' + strVal
+ self.appendSection('enum', body)
+ #
+ # Command generation
+ def genCmd(self, cmdinfo, name):
+ OutputGenerator.genCmd(self, cmdinfo, name)
+ #
+ decls = self.makeCDecls(cmdinfo.elem)
+ self.appendSection('command', decls[0] + '\n')
+ if (self.genOpts.genFuncPointers):
+ self.appendSection('commandPointer', decls[1])
+
+# DocOutputGenerator - subclass of OutputGenerator.
+# Generates AsciiDoc includes with C-language API interfaces, for reference
+# pages and the Vulkan specification. Similar to COutputGenerator, but
+# each interface is written into a different file as determined by the
+# options, only actual C types are emitted, and none of the boilerplate
+# preprocessor code is emitted.
+#
+# ---- methods ----
+# DocOutputGenerator(errFile, warnFile, diagFile) - args as for
+# OutputGenerator. Defines additional internal state.
+# ---- methods overriding base class ----
+# beginFile(genOpts)
+# endFile()
+# beginFeature(interface, emit)
+# endFeature()
+# genType(typeinfo,name)
+# genStruct(typeinfo,name)
+# genGroup(groupinfo,name)
+# genEnum(enuminfo, name)
+# genCmd(cmdinfo)
+class DocOutputGenerator(OutputGenerator):
+ """Generate specified API interfaces in a specific style, such as a C header"""
+ def __init__(self,
+ errFile = sys.stderr,
+ warnFile = sys.stderr,
+ diagFile = sys.stdout):
+ OutputGenerator.__init__(self, errFile, warnFile, diagFile)
+ #
+ def beginFile(self, genOpts):
+ OutputGenerator.beginFile(self, genOpts)
+ def endFile(self):
+ OutputGenerator.endFile(self)
+ def beginFeature(self, interface, emit):
+ # Start processing in superclass
+ OutputGenerator.beginFeature(self, interface, emit)
+ def endFeature(self):
+ # Finish processing in superclass
+ OutputGenerator.endFeature(self)
+ #
+ # Generate an include file
+ #
+ # directory - subdirectory to put file in
+ # basename - base name of the file
+ # contents - contents of the file (Asciidoc boilerplate aside)
+ def writeInclude(self, directory, basename, contents):
+ # Create file
+ filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
+ self.logMsg('diag', '# Generating include file:', filename)
+ fp = open(filename, 'w')
+ # Asciidoc anchor
+ write('[[{0},{0}]]'.format(basename), file=fp)
+ write('["source","{basebackend@docbook:c++:cpp}",title=""]', file=fp)
+ write('------------------------------------------------------------------------------', file=fp)
+ write(contents, file=fp)
+ write('------------------------------------------------------------------------------', file=fp)
+ fp.close()
+ #
+ # Type generation
+ def genType(self, typeinfo, name):
+ OutputGenerator.genType(self, typeinfo, name)
+ typeElem = typeinfo.elem
+ # If the type is a struct type, traverse the imbedded <member> tags
+ # generating a structure. Otherwise, emit the tag text.
+ category = typeElem.get('category')
+ if (category == 'struct' or category == 'union'):
+ self.genStruct(typeinfo, name)
+ else:
+ # Replace <apientry /> tags with an APIENTRY-style string
+ # (from self.genOpts). Copy other text through unchanged.
+ # If the resulting text is an empty string, don't emit it.
+ s = noneStr(typeElem.text)
+ for elem in typeElem:
+ if (elem.tag == 'apientry'):
+ s += self.genOpts.apientry + noneStr(elem.tail)
+ else:
+ s += noneStr(elem.text) + noneStr(elem.tail)
+ if (len(s) > 0):
+ if (category == 'bitmask'):
+ self.writeInclude('flags', name, s + '\n')
+ elif (category == 'enum'):
+ self.writeInclude('enums', name, s + '\n')
+ elif (category == 'funcpointer'):
+ self.writeInclude('funcpointers', name, s+ '\n')
+ else:
+ self.logMsg('diag', '# NOT writing include file for type:',
+ name, 'category: ', category)
+ else:
+ self.logMsg('diag', '# NOT writing empty include file for type', name)
+ #
+ # Struct (e.g. C "struct" type) generation.
+ # This is a special case of the <type> tag where the contents are
+ # interpreted as a set of <member> tags instead of freeform C
+ # C type declarations. The <member> tags are just like <param>
+ # tags - they are a declaration of a struct or union member.
+ # Only simple member declarations are supported (no nested
+ # structs etc.)
+ def genStruct(self, typeinfo, typeName):
+ OutputGenerator.genStruct(self, typeinfo, typeName)
+ s = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
+ # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
+ targetLen = 0;
+ for member in typeinfo.elem.findall('.//member'):
+ targetLen = max(targetLen, self.getCParamTypeLength(member))
+ for member in typeinfo.elem.findall('.//member'):
+ s += self.makeCParamDecl(member, targetLen + 4)
+ s += ';\n'
+ s += '} ' + typeName + ';'
+ self.writeInclude('structs', typeName, s)
+ #
+ # Group (e.g. C "enum" type) generation.
+ # These are concatenated together with other types.
+ def genGroup(self, groupinfo, groupName):
+ OutputGenerator.genGroup(self, groupinfo, groupName)
+ groupElem = groupinfo.elem
+ # See if this group needs min/max/num/padding at end
+ expand = self.genOpts.expandEnumerants and ('expand' in groupElem.keys())
+ if (expand):
+ expandPrefix = groupElem.get('expand')
+ # Prefix
+ s = "typedef enum " + groupName + " {\n"
+
+ # Loop over the nested 'enum' tags. Keep track of the minimum and
+ # maximum numeric values, if they can be determined.
+ minName = None
+ for elem in groupElem.findall('enum'):
+ # Convert the value to an integer and use that to track min/max.
+ # Values of form -(number) are accepted but nothing more complex.
+ # Should catch exceptions here for more complex constructs. Not yet.
+ (numVal,strVal) = self.enumToValue(elem, True)
+ name = elem.get('name')
+ s += " " + name + " = " + strVal + ",\n"
+ if (expand and elem.get('extends') is None):
+ if (minName == None):
+ minName = maxName = name
+ minValue = maxValue = numVal
+ elif (numVal < minValue):
+ minName = name
+ minValue = numVal
+ elif (numVal > maxValue):
+ maxName = name
+ maxValue = numVal
+ # Generate min/max value tokens and a range-padding enum. Need some
+ # additional padding to generate correct names...
+ if (expand):
+ s += "\n"
+ s += " " + expandPrefix + "_BEGIN_RANGE = " + minName + ",\n"
+ s += " " + expandPrefix + "_END_RANGE = " + maxName + ",\n"
+ s += " " + expandPrefix + "_NUM = (" + maxName + " - " + minName + " + 1),\n"
+ s += " " + expandPrefix + "_MAX_ENUM = 0x7FFFFFFF\n"
+ # Postfix
+ s += "} " + groupName + ";"
+ self.writeInclude('enums', groupName, s)
+ # Enumerant generation
+ # <enum> tags may specify their values in several ways, but are usually
+ # just integers.
+ def genEnum(self, enuminfo, name):
+ OutputGenerator.genEnum(self, enuminfo, name)
+ (numVal,strVal) = self.enumToValue(enuminfo.elem, False)
+ s = '#define ' + name.ljust(33) + ' ' + strVal
+ self.logMsg('diag', '# NOT writing compile-time constant', name)
+ # self.writeInclude('consts', name, s)
+ #
+ # Command generation
+ def genCmd(self, cmdinfo, name):
+ OutputGenerator.genCmd(self, cmdinfo, name)
+ #
+ decls = self.makeCDecls(cmdinfo.elem)
+ self.writeInclude('protos', name, decls[0])
+
+# PyOutputGenerator - subclass of OutputGenerator.
+# Generates Python data structures describing API names.
+# Similar to DocOutputGenerator, but writes a single
+# file.
+#
+# ---- methods ----
+# PyOutputGenerator(errFile, warnFile, diagFile) - args as for
+# OutputGenerator. Defines additional internal state.
+# ---- methods overriding base class ----
+# beginFile(genOpts)
+# endFile()
+# genType(typeinfo,name)
+# genStruct(typeinfo,name)
+# genGroup(groupinfo,name)
+# genEnum(enuminfo, name)
+# genCmd(cmdinfo)
+class PyOutputGenerator(OutputGenerator):
+ """Generate specified API interfaces in a specific style, such as a C header"""
+ def __init__(self,
+ errFile = sys.stderr,
+ warnFile = sys.stderr,
+ diagFile = sys.stdout):
+ OutputGenerator.__init__(self, errFile, warnFile, diagFile)
+ #
+ def beginFile(self, genOpts):
+ OutputGenerator.beginFile(self, genOpts)
+ for dict in [ 'flags', 'enums', 'structs', 'consts', 'enums',
+ 'consts', 'protos', 'funcpointers' ]:
+ write(dict, '= {}', file=self.outFile)
+ def endFile(self):
+ OutputGenerator.endFile(self)
+ #
+ # Add a name from the interface
+ #
+ # dict - type of name (see beginFile above)
+ # name - name to add
+ # value - A serializable Python value for the name
+ def addName(self, dict, name, value=None):
+ write(dict + "['" + name + "'] = ", value, file=self.outFile)
+ #
+ # Type generation
+ # For 'struct' or 'union' types, defer to genStruct() to
+ # add to the dictionary.
+ # For 'bitmask' types, add the type name to the 'flags' dictionary,
+ # with the value being the corresponding 'enums' name defining
+ # the acceptable flag bits.
+ # For 'enum' types, add the type name to the 'enums' dictionary,
+ # with the value being '@STOPHERE@' (because this case seems
+ # never to happen).
+ # For 'funcpointer' types, add the type name to the 'funcpointers'
+ # dictionary.
+ # For 'handle' and 'define' types, add the handle or #define name
+ # to the 'struct' dictionary, because that's how the spec sources
+ # tag these types even though they aren't structs.
+ def genType(self, typeinfo, name):
+ OutputGenerator.genType(self, typeinfo, name)
+ typeElem = typeinfo.elem
+ # If the type is a struct type, traverse the imbedded <member> tags
+ # generating a structure. Otherwise, emit the tag text.
+ category = typeElem.get('category')
+ if (category == 'struct' or category == 'union'):
+ self.genStruct(typeinfo, name)
+ else:
+ # Extract the type name
+ # (from self.genOpts). Copy other text through unchanged.
+ # If the resulting text is an empty string, don't emit it.
+ count = len(noneStr(typeElem.text))
+ for elem in typeElem:
+ count += len(noneStr(elem.text)) + len(noneStr(elem.tail))
+ if (count > 0):
+ if (category == 'bitmask'):
+ requiredEnum = typeElem.get('requires')
+ self.addName('flags', name, enquote(requiredEnum))
+ elif (category == 'enum'):
+ # This case never seems to come up!
+ # @enums C 'enum' name Dictionary of enumerant names
+ self.addName('enums', name, enquote('@STOPHERE@'))
+ elif (category == 'funcpointer'):
+ self.addName('funcpointers', name, None)
+ elif (category == 'handle' or category == 'define'):
+ self.addName('structs', name, None)
+ else:
+ write('# Unprocessed type:', name, 'category:', category, file=self.outFile)
+ else:
+ write('# Unprocessed type:', name, file=self.outFile)
+ #
+ # Struct (e.g. C "struct" type) generation.
+ #
+ # Add the struct name to the 'structs' dictionary, with the
+ # value being an ordered list of the struct member names.
+ def genStruct(self, typeinfo, typeName):
+ OutputGenerator.genStruct(self, typeinfo, typeName)
+
+ members = [member.text for member in typeinfo.elem.findall('.//member/name')]
+ self.addName('structs', typeName, members)
+ #
+ # Group (e.g. C "enum" type) generation.
+ # These are concatenated together with other types.
+ #
+ # Add the enum type name to the 'enums' dictionary, with
+ # the value being an ordered list of the enumerant names.
+ # Add each enumerant name to the 'consts' dictionary, with
+ # the value being the enum type the enumerant is part of.
+ def genGroup(self, groupinfo, groupName):
+ OutputGenerator.genGroup(self, groupinfo, groupName)
+ groupElem = groupinfo.elem
+
+ # @enums C 'enum' name Dictionary of enumerant names
+ # @consts C enumerant/const name Name of corresponding 'enums' key
+
+ # Loop over the nested 'enum' tags. Keep track of the minimum and
+ # maximum numeric values, if they can be determined.
+ enumerants = [elem.get('name') for elem in groupElem.findall('enum')]
+ for name in enumerants:
+ self.addName('consts', name, enquote(groupName))
+ self.addName('enums', groupName, enumerants)
+ # Enumerant generation (compile-time constants)
+ #
+ # Add the constant name to the 'consts' dictionary, with the
+ # value being None to indicate that the constant isn't
+ # an enumeration value.
+ def genEnum(self, enuminfo, name):
+ OutputGenerator.genEnum(self, enuminfo, name)
+
+ # @consts C enumerant/const name Name of corresponding 'enums' key
+
+ self.addName('consts', name, None)
+ #
+ # Command generation
+ #
+ # Add the command name to the 'protos' dictionary, with the
+ # value being an ordered list of the parameter names.
+ def genCmd(self, cmdinfo, name):
+ OutputGenerator.genCmd(self, cmdinfo, name)
+
+ params = [param.text for param in cmdinfo.elem.findall('param/name')]
+ self.addName('protos', name, params)
+
+# ValidityOutputGenerator - subclass of OutputGenerator.
+# Generates AsciiDoc includes of valid usage information, for reference
+# pages and the Vulkan specification. Similar to DocOutputGenerator.
+#
+# ---- methods ----
+# ValidityOutputGenerator(errFile, warnFile, diagFile) - args as for
+# OutputGenerator. Defines additional internal state.
+# ---- methods overriding base class ----
+# beginFile(genOpts)
+# endFile()
+# beginFeature(interface, emit)
+# endFeature()
+# genCmd(cmdinfo)
+class ValidityOutputGenerator(OutputGenerator):
+ """Generate specified API interfaces in a specific style, such as a C header"""
+ def __init__(self,
+ errFile = sys.stderr,
+ warnFile = sys.stderr,
+ diagFile = sys.stdout):
+ OutputGenerator.__init__(self, errFile, warnFile, diagFile)
+
+ def beginFile(self, genOpts):
+ OutputGenerator.beginFile(self, genOpts)
+ def endFile(self):
+ OutputGenerator.endFile(self)
+ def beginFeature(self, interface, emit):
+ # Start processing in superclass
+ OutputGenerator.beginFeature(self, interface, emit)
+ def endFeature(self):
+ # Finish processing in superclass
+ OutputGenerator.endFeature(self)
+
+ def makeParameterName(self, name):
+ return 'pname:' + name
+
+ def makeStructName(self, name):
+ return 'sname:' + name
+
+ def makeBaseTypeName(self, name):
+ return 'basetype:' + name
+
+ def makeEnumerationName(self, name):
+ return 'elink:' + name
+
+ def makeEnumerantName(self, name):
+ return 'ename:' + name
+
+ def makeFLink(self, name):
+ return 'flink:' + name
+
+ #
+ # Generate an include file
+ #
+ # directory - subdirectory to put file in
+ # basename - base name of the file
+ # contents - contents of the file (Asciidoc boilerplate aside)
+ def writeInclude(self, directory, basename, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes):
+ # Create file
+ filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
+ self.logMsg('diag', '# Generating include file:', filename)
+ fp = open(filename, 'w')
+ # Asciidoc anchor
+
+ # Valid Usage
+ if validity is not None:
+ write('.Valid Usage', file=fp)
+ write('*' * 80, file=fp)
+ write(validity, file=fp, end='')
+ write('*' * 80, file=fp)
+ write('', file=fp)
+
+ # Host Synchronization
+ if threadsafety is not None:
+ write('.Host Synchronization', file=fp)
+ write('*' * 80, file=fp)
+ write(threadsafety, file=fp, end='')
+ write('*' * 80, file=fp)
+ write('', file=fp)
+
+ # Command Properties - contained within a block, to avoid table numbering
+ if commandpropertiesentry is not None:
+ write('.Command Properties', file=fp)
+ write('*' * 80, file=fp)
+ write('[options="header", width="100%"]', file=fp)
+ write('|=====================', file=fp)
+ write('|Command Buffer Levels|Render Pass Scope|Supported Queue Types', file=fp)
+ write(commandpropertiesentry, file=fp)
+ write('|=====================', file=fp)
+ write('*' * 80, file=fp)
+ write('', file=fp)
+
+ # Success Codes - contained within a block, to avoid table numbering
+ if successcodes is not None or errorcodes is not None:
+ write('.Return Codes', file=fp)
+ write('*' * 80, file=fp)
+ if successcodes is not None:
+ write('<<fundamentals-successcodes,Success>>::', file=fp)
+ write(successcodes, file=fp)
+ if errorcodes is not None:
+ write('<<fundamentals-errorcodes,Failure>>::', file=fp)
+ write(errorcodes, file=fp)
+ write('*' * 80, file=fp)
+ write('', file=fp)
+
+ fp.close()
+
+ #
+ # Check if the parameter passed in is a pointer
+ def paramIsPointer(self, param):
+ ispointer = False
+ paramtype = param.find('type')
+ if paramtype.tail is not None and '*' in paramtype.tail:
+ ispointer = True
+
+ return ispointer
+
+ #
+ # Check if the parameter passed in is a static array
+ def paramIsStaticArray(self, param):
+ if param.find('name').tail is not None:
+ if param.find('name').tail[0] == '[':
+ return True
+
+ #
+ # Get the length of a parameter that's been identified as a static array
+ def staticArrayLength(self, param):
+ paramname = param.find('name')
+ paramenumsize = param.find('enum')
+
+ if paramenumsize is not None:
+ return paramenumsize.text
+ else:
+ return paramname.tail[1:-1]
+
+ #
+ # Check if the parameter passed in is a pointer to an array
+ def paramIsArray(self, param):
+ return param.attrib.get('len') is not None
+
+ #
+ # Get the parent of a handle object
+ def getHandleParent(self, typename):
+ types = self.registry.findall("types/type")
+ for elem in types:
+ if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
+ return elem.attrib.get('parent')
+
+ #
+ # Check if a parent object is dispatchable or not
+ def isHandleTypeDispatchable(self, handlename):
+ handle = self.registry.find("types/type/[name='" + handlename + "'][@category='handle']")
+ if handle is not None and handle.find('type').text == 'VK_DEFINE_HANDLE':
+ return True
+ else:
+ return False
+
+ def isHandleOptional(self, param, params):
+
+ # See if the handle is optional
+ isOptional = False
+
+ # Simple, if it's optional, return true
+ if param.attrib.get('optional') is not None:
+ return True
+
+ # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
+ if param.attrib.get('noautovalidity') is not None:
+ return True
+
+ # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
+ if self.paramIsArray(param):
+ lengths = param.attrib.get('len').split(',')
+ for length in lengths:
+ if (length) != 'null-terminated' and (length) != '1':
+ for otherparam in params:
+ if otherparam.find('name').text == length:
+ if otherparam.attrib.get('optional') is not None:
+ return True
+
+ return False
+ #
+ # Get the category of a type
+ def getTypeCategory(self, typename):
+ types = self.registry.findall("types/type")
+ for elem in types:
+ if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
+ return elem.attrib.get('category')
+
+ #
+ # Make a chunk of text for the end of a parameter if it is an array
+ def makeAsciiDocPreChunk(self, param, params):
+ paramname = param.find('name')
+ paramtype = param.find('type')
+
+ # General pre-amble. Check optionality and add stuff.
+ asciidoc = '* '
+
+ if self.paramIsStaticArray(param):
+ asciidoc += 'Any given element of '
+
+ elif self.paramIsArray(param):
+ lengths = param.attrib.get('len').split(',')
+
+ # 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
+ optionallengths = []
+ for length in lengths:
+ if (length) != 'null-terminated' and (length) != '1':
+ for otherparam in params:
+ if otherparam.find('name').text == length:
+ if otherparam.attrib.get('optional') is not None:
+ if self.paramIsPointer(otherparam):
+ optionallengths.append('the value referenced by ' + self.makeParameterName(length))
+ else:
+ optionallengths.append(self.makeParameterName(length))
+
+ # Document that these arrays may be ignored if any of the length values are 0
+ if len(optionallengths) != 0 or param.attrib.get('optional') is not None:
+ asciidoc += 'If '
+
+
+ if len(optionallengths) != 0:
+ if len(optionallengths) == 1:
+
+ asciidoc += optionallengths[0]
+ asciidoc += ' is '
+
+ else:
+ asciidoc += ' or '.join(optionallengths)
+ asciidoc += ' are '
+
+ asciidoc += 'not `0`, '
+
+ if len(optionallengths) != 0 and param.attrib.get('optional') is not None:
+ asciidoc += 'and '
+
+ if param.attrib.get('optional') is not None:
+ asciidoc += self.makeParameterName(paramname.text)
+ asciidoc += ' is not `NULL`, '
+
+ elif param.attrib.get('optional') is not None:
+ # Don't generate this stub for bitflags
+ if self.getTypeCategory(paramtype.text) != 'bitmask':
+ if param.attrib.get('optional').split(',')[0] == 'true':
+ asciidoc += 'If '
+ asciidoc += self.makeParameterName(paramname.text)
+ asciidoc += ' is not '
+ if self.paramIsArray(param) or self.paramIsPointer(param) or self.isHandleTypeDispatchable(paramtype.text):
+ asciidoc += '`NULL`'
+ elif self.getTypeCategory(paramtype.text) == 'handle':
+ asciidoc += 'sname:VK_NULL_HANDLE'
+ else:
+ asciidoc += '`0`'
+
+ asciidoc += ', '
+
+ return asciidoc
+
+ #
+ # Make the generic asciidoc line chunk portion used for all parameters.
+ # May return an empty string if nothing to validate.
+ def createValidationLineForParameterIntroChunk(self, param, params, typetext):
+ asciidoc = ''
+ paramname = param.find('name')
+ paramtype = param.find('type')
+
+ asciidoc += self.makeAsciiDocPreChunk(param, params)
+
+ asciidoc += self.makeParameterName(paramname.text)
+ asciidoc += ' must: be '
+
+ if self.paramIsArray(param):
+ # Arrays. These are hard to get right, apparently
+
+ lengths = param.attrib.get('len').split(',')
+
+ if (lengths[0]) == 'null-terminated':
+ asciidoc += 'a null-terminated '
+ elif (lengths[0]) == '1':
+ asciidoc += 'a pointer to '
+ else:
+ asciidoc += 'a pointer to an array of '
+
+ # Handle equations, which are currently denoted with latex
+ if 'latexmath:' in lengths[0]:
+ asciidoc += lengths[0]
+ else:
+ asciidoc += self.makeParameterName(lengths[0])
+ asciidoc += ' '
+
+ for length in lengths[1:]:
+ 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.
+ asciidoc += 'null-terminated '
+ elif (length) == '1':
+ asciidoc += 'pointers to '
+ else:
+ asciidoc += 'pointers to arrays of '
+ # Handle equations, which are currently denoted with latex
+ if 'latex:' in length:
+ asciidoc += length
+ else:
+ asciidoc += self.makeParameterName(length)
+ asciidoc += ' '
+
+ # Void pointers don't actually point at anything - remove the word "to"
+ if paramtype.text == 'void':
+ if lengths[-1] == '1':
+ if len(lengths) > 1:
+ asciidoc = asciidoc[:-5] # Take care of the extra s added by the post array chunk function. #HACK#
+ else:
+ asciidoc = asciidoc[:-4]
+ else:
+ # An array of void values is a byte array.
+ asciidoc += 'byte'
+
+ elif paramtype.text == 'char':
+ # A null terminated array of chars is a string
+ if lengths[-1] == 'null-terminated':
+ asciidoc += 'string'
+ else:
+ # Else it's just a bunch of chars
+ asciidoc += 'char value'
+ elif param.text is not None:
+ # If a value is "const" that means it won't get modified, so it must be valid going into the function.
+ if 'const' in param.text:
+ typecategory = self.getTypeCategory(paramtype.text)
+ if (typecategory != 'struct' and typecategory != 'union' and typecategory != 'basetype' and typecategory is not None) or not self.isStructAlwaysValid(paramtype.text):
+ asciidoc += 'valid '
+
+ asciidoc += typetext
+
+ # pluralize
+ if len(lengths) > 1 or (lengths[0] != '1' and lengths[0] != 'null-terminated'):
+ asciidoc += 's'
+
+ elif self.paramIsPointer(param):
+ # Handle pointers - which are really special case arrays (i.e. they don't have a length)
+ pointercount = paramtype.tail.count('*')
+
+ # Could be multi-level pointers (e.g. ppData - pointer to a pointer). Handle that.
+ for i in range(0, pointercount):
+ asciidoc += 'a pointer to '
+
+ if paramtype.text == 'void':
+ # If there's only one pointer, it's optional, and it doesn't point at anything in particular - we don't need any language.
+ if pointercount == 1 and param.attrib.get('optional') is not None:
+ return '' # early return
+ else:
+ # Pointer to nothing in particular - delete the " to " portion
+ asciidoc = asciidoc[:-4]
+ else:
+ # Add an article for English semantic win
+ asciidoc += 'a '
+
+ # If a value is "const" that means it won't get modified, so it must be valid going into the function.
+ if param.text is not None and paramtype.text != 'void':
+ if 'const' in param.text:
+ asciidoc += 'valid '
+
+ asciidoc += typetext
+
+ else:
+ # Non-pointer, non-optional things must be valid
+ asciidoc += 'a valid '
+ asciidoc += typetext
+
+ if asciidoc != '':
+ asciidoc += '\n'
+
+ # Add additional line for non-optional bitmasks
+ if self.getTypeCategory(paramtype.text) == 'bitmask':
+ if param.attrib.get('optional') is None:
+ asciidoc += '* '
+ if self.paramIsArray(param):
+ asciidoc += 'Each element of '
+ asciidoc += 'pname:'
+ asciidoc += paramname.text
+ asciidoc += ' mustnot: be `0`'
+ asciidoc += '\n'
+
+ return asciidoc
+
+ def makeAsciiDocLineForParameter(self, param, params, typetext):
+ if param.attrib.get('noautovalidity') is not None:
+ return ''
+ asciidoc = self.createValidationLineForParameterIntroChunk(param, params, typetext)
+
+ return asciidoc
+
+ # Try to do check if a structure is always considered valid (i.e. there's no rules to its acceptance)
+ def isStructAlwaysValid(self, structname):
+
+ struct = self.registry.find("types/type[@name='" + structname + "']")
+
+ params = struct.findall('member')
+ validity = struct.find('validity')
+
+ if validity is not None:
+ return False
+
+ for param in params:
+ paramname = param.find('name')
+ paramtype = param.find('type')
+ typecategory = self.getTypeCategory(paramtype.text)
+
+ if paramname.text == 'pNext':
+ return False
+
+ if paramname.text == 'sType':
+ return False
+
+ if paramtype.text == 'void' or paramtype.text == 'char' or self.paramIsArray(param) or self.paramIsPointer(param):
+ if self.makeAsciiDocLineForParameter(param, params, '') != '':
+ return False
+ elif typecategory == 'handle' or typecategory == 'enum' or typecategory == 'bitmask' or param.attrib.get('returnedonly') == 'true':
+ return False
+ elif typecategory == 'struct' or typecategory == 'union':
+ if self.isStructAlwaysValid(paramtype.text) is False:
+ return False
+
+ return True
+
+ #
+ # Make an entire asciidoc line for a given parameter
+ def createValidationLineForParameter(self, param, params, typecategory):
+ asciidoc = ''
+ paramname = param.find('name')
+ paramtype = param.find('type')
+
+ if paramtype.text == 'void' or paramtype.text == 'char':
+ # Chars and void are special cases - needs care inside the generator functions
+ # A null-terminated char array is a string, else it's chars.
+ # An array of void values is a byte array, a void pointer is just a pointer to nothing in particular
+ asciidoc += self.makeAsciiDocLineForParameter(param, params, '')
+ elif typecategory == 'bitmask':
+ bitsname = paramtype.text.replace('Flags', 'FlagBits')
+ if self.registry.find("enums[@name='" + bitsname + "']") is None:
+ asciidoc += '* '
+ asciidoc += self.makeParameterName(paramname.text)
+ asciidoc += ' must: be `0`'
+ asciidoc += '\n'
+ else:
+ if self.paramIsArray(param):
+ asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combinations of ' + self.makeEnumerationName(bitsname) + ' value')
+ else:
+ asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combination of ' + self.makeEnumerationName(bitsname) + ' values')
+ elif typecategory == 'handle':
+ asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' handle')
+ elif typecategory == 'enum':
+ asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeEnumerationName(paramtype.text) + ' value')
+ elif typecategory == 'struct':
+ if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text):
+ asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' structure')
+ elif typecategory == 'union':
+ if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text):
+ asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' union')
+ elif self.paramIsArray(param) or self.paramIsPointer(param):
+ asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeBaseTypeName(paramtype.text) + ' value')
+
+ return asciidoc
+
+ #
+ # Make an asciidoc validity entry for a handle's parent object
+ def makeAsciiDocHandleParent(self, param, params):
+ asciidoc = ''
+ paramname = param.find('name')
+ paramtype = param.find('type')
+
+ # Deal with handle parents
+ handleparent = self.getHandleParent(paramtype.text)
+ if handleparent is not None:
+ parentreference = None
+ for otherparam in params:
+ if otherparam.find('type').text == handleparent:
+ parentreference = otherparam.find('name').text
+ if parentreference is not None:
+ asciidoc += '* '
+
+ if self.isHandleOptional(param, params):
+ if self.paramIsArray(param):
+ asciidoc += 'Each element of '
+ asciidoc += self.makeParameterName(paramname.text)
+ asciidoc += ' that is a valid handle'
+ else:
+ asciidoc += 'If '
+ asciidoc += self.makeParameterName(paramname.text)
+ asciidoc += ' is a valid handle, it'
+ else:
+ if self.paramIsArray(param):
+ asciidoc += 'Each element of '
+ asciidoc += self.makeParameterName(paramname.text)
+ asciidoc += ' must: have been created, allocated or retrieved from '
+ asciidoc += self.makeParameterName(parentreference)
+
+ asciidoc += '\n'
+ return asciidoc
+
+ #
+ # Generate an asciidoc validity line for the sType value of a struct
+ def makeStructureType(self, blockname, param):
+ asciidoc = '* '
+ paramname = param.find('name')
+ paramtype = param.find('type')
+
+ asciidoc += self.makeParameterName(paramname.text)
+ asciidoc += ' must: be '
+
+ structuretype = ''
+ for elem in re.findall(r'(([A-Z][a-z]+)|([A-Z][A-Z]+))', blockname):
+ if elem[0] == 'Vk':
+ structuretype += 'VK_STRUCTURE_TYPE_'
+ else:
+ structuretype += elem[0].upper()
+ structuretype += '_'
+
+ asciidoc += self.makeEnumerantName(structuretype[:-1])
+ asciidoc += '\n'
+
+ return asciidoc
+
+ #
+ # Generate an asciidoc validity line for the pNext value of a struct
+ def makeStructureExtensionPointer(self, param):
+ asciidoc = '* '
+ paramname = param.find('name')
+ paramtype = param.find('type')
+
+ asciidoc += self.makeParameterName(paramname.text)
+
+ validextensionstructs = param.attrib.get('validextensionstructs')
+ if validextensionstructs is None:
+ asciidoc += ' must: be `NULL`'
+ else:
+ extensionstructs = validextensionstructs.split(',')
+ asciidoc += ' must: point to one of ' + extensionstructs[:-1].join(', ') + ' or ' + extensionstructs[-1] + 'if the extension that introduced them is enabled '
+
+ asciidoc += '\n'
+
+ return asciidoc
+
+ #
+ # Generate all the valid usage information for a given struct or command
+ def makeValidUsageStatements(self, cmd, blockname, params, usages):
+ # Start the asciidoc block for this
+ asciidoc = ''
+
+ handles = []
+ anyparentedhandlesoptional = False
+ parentdictionary = {}
+ arraylengths = set()
+ for param in params:
+ paramname = param.find('name')
+ paramtype = param.find('type')
+
+ # Get the type's category
+ typecategory = self.getTypeCategory(paramtype.text)
+
+ # Generate language to independently validate a parameter
+ if paramtype.text == 'VkStructureType' and paramname.text == 'sType':
+ asciidoc += self.makeStructureType(blockname, param)
+ elif paramtype.text == 'void' and paramname.text == 'pNext':
+ asciidoc += self.makeStructureExtensionPointer(param)
+ else:
+ asciidoc += self.createValidationLineForParameter(param, params, typecategory)
+
+ # Ensure that any parenting is properly validated, and list that a handle was found
+ if typecategory == 'handle':
+ # Don't detect a parent for return values!
+ if not self.paramIsPointer(param) or (param.text is not None and 'const' in param.text):
+ parent = self.getHandleParent(paramtype.text)
+ if parent is not None:
+ handles.append(param)
+
+ # If any param is optional, it affects the output
+ if self.isHandleOptional(param, params):
+ anyparentedhandlesoptional = True
+
+ # Find the first dispatchable parent
+ ancestor = parent
+ while ancestor is not None and not self.isHandleTypeDispatchable(ancestor):
+ ancestor = self.getHandleParent(ancestor)
+
+ # If one was found, add this parameter to the parent dictionary
+ if ancestor is not None:
+ if ancestor not in parentdictionary:
+ parentdictionary[ancestor] = []
+
+ if self.paramIsArray(param):
+ parentdictionary[ancestor].append('the elements of ' + self.makeParameterName(paramname.text))
+ else:
+ parentdictionary[ancestor].append(self.makeParameterName(paramname.text))
+
+ # Get the array length for this parameter
+ arraylength = param.attrib.get('len')
+ if arraylength is not None:
+ for onelength in arraylength.split(','):
+ arraylengths.add(onelength)
+
+ # For any vkQueue* functions, there might be queue type data
+ if 'vkQueue' in blockname:
+ # The queue type must be valid
+ queuetypes = cmd.attrib.get('queues')
+ if queuetypes is not None:
+ queuebits = []
+ for queuetype in re.findall(r'([^,]+)', queuetypes):
+ queuebits.append(queuetype.replace('_',' '))
+
+ asciidoc += '* '
+ asciidoc += 'The pname:queue must: support '
+ if len(queuebits) == 1:
+ asciidoc += queuebits[0]
+ else:
+ asciidoc += (', ').join(queuebits[:-1])
+ asciidoc += ' or '
+ asciidoc += queuebits[-1]
+ asciidoc += ' operations'
+ asciidoc += '\n'
+
+ if 'vkCmd' in blockname:
+ # The commandBuffer parameter must be being recorded
+ asciidoc += '* '
+ asciidoc += 'pname:commandBuffer must: be in the recording state'
+ asciidoc += '\n'
+
+ # The queue type must be valid
+ queuetypes = cmd.attrib.get('queues')
+ queuebits = []
+ for queuetype in re.findall(r'([^,]+)', queuetypes):
+ queuebits.append(queuetype.replace('_',' '))
+
+ asciidoc += '* '
+ asciidoc += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support '
+ if len(queuebits) == 1:
+ asciidoc += queuebits[0]
+ else:
+ asciidoc += (', ').join(queuebits[:-1])
+ asciidoc += ' or '
+ asciidoc += queuebits[-1]
+ asciidoc += ' operations'
+ asciidoc += '\n'
+
+ # Must be called inside/outside a renderpass appropriately
+ renderpass = cmd.attrib.get('renderpass')
+
+ if renderpass != 'both':
+ asciidoc += '* This command must: only be called '
+ asciidoc += renderpass
+ asciidoc += ' of a render pass instance'
+ asciidoc += '\n'
+
+ # Must be in the right level command buffer
+ cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
+
+ if cmdbufferlevel != 'primary,secondary':
+ asciidoc += '* pname:commandBuffer must: be a '
+ asciidoc += cmdbufferlevel
+ asciidoc += ' sname:VkCommandBuffer'
+ asciidoc += '\n'
+
+ # Any non-optional arraylengths should specify they must be greater than 0
+ for param in params:
+ paramname = param.find('name')
+
+ for arraylength in arraylengths:
+ if paramname.text == arraylength and param.attrib.get('optional') is None:
+ # Get all the array dependencies
+ arrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
+
+ # Get all the optional array dependencies, including those not generating validity for some reason
+ optionalarrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
+ optionalarrays.extend(cmd.findall("param/[@len='" + arraylength + "'][@noautovalidity='true']"))
+
+ asciidoc += '* '
+
+ # Allow lengths to be arbitrary if all their dependents are optional
+ if len(optionalarrays) == len(arrays) and len(optionalarrays) != 0:
+ asciidoc += 'If '
+ if len(optionalarrays) > 1:
+ asciidoc += 'any of '
+
+ for array in optionalarrays[:-1]:
+ asciidoc += self.makeParameterName(optionalarrays.find('name').text)
+ asciidoc += ', '
+
+ if len(optionalarrays) > 1:
+ asciidoc += 'and '
+ asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
+ asciidoc += ' are '
+ else:
+ asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
+ asciidoc += ' is '
+
+ asciidoc += 'not `NULL`, '
+
+ if self.paramIsPointer(param):
+ asciidoc += 'the value referenced by '
+ else:
+ asciidoc += 'the value of '
+
+ elif self.paramIsPointer(param):
+ asciidoc += 'The value referenced by '
+ else:
+ asciidoc += 'The value of '
+
+ asciidoc += self.makeParameterName(arraylength)
+ asciidoc += ' must: be greater than `0`'
+ asciidoc += '\n'
+
+ # Find the parents of all objects referenced in this command
+ for param in handles:
+ asciidoc += self.makeAsciiDocHandleParent(param, params)
+
+ # Find the common ancestors of objects
+ noancestorscount = 0
+ while noancestorscount < len(parentdictionary):
+ noancestorscount = 0
+ oldparentdictionary = parentdictionary.copy()
+ for parent in oldparentdictionary.items():
+ ancestor = self.getHandleParent(parent[0])
+
+ while ancestor is not None and ancestor not in parentdictionary:
+ ancestor = self.getHandleParent(ancestor)
+
+ if ancestor is not None:
+ parentdictionary[ancestor] += parentdictionary.pop(parent[0])
+ else:
+ # No ancestors possible - so count it up
+ noancestorscount += 1
+
+ # Add validation language about common ancestors
+ for parent in parentdictionary.items():
+ if len(parent[1]) > 1:
+ parentlanguage = '* '
+
+ parentlanguage += 'Each of '
+ parentlanguage += ", ".join(parent[1][:-1])
+ parentlanguage += ' and '
+ parentlanguage += parent[1][-1]
+ if anyparentedhandlesoptional is True:
+ parentlanguage += ' that are valid handles'
+ parentlanguage += ' must: have been created, allocated or retrieved from the same '
+ parentlanguage += self.makeStructName(parent[0])
+ parentlanguage += '\n'
+
+ # Capitalize and add to the main language
+ asciidoc += parentlanguage
+
+ # Add in any plain-text validation language that's in the xml
+ for usage in usages:
+ asciidoc += '* '
+ asciidoc += usage.text
+ asciidoc += '\n'
+
+ # In case there's nothing to report, return None
+ if asciidoc == '':
+ return None
+ # Delimit the asciidoc block
+ return asciidoc
+
+ def makeThreadSafetyBlock(self, cmd, paramtext):
+ """Generate C function pointer typedef for <command> Element"""
+ paramdecl = ''
+
+ # For any vkCmd* functions, the commandBuffer parameter must be being recorded
+ if cmd.find('proto/name') is not None and 'vkCmd' in cmd.find('proto/name'):
+ paramdecl += '* '
+ paramdecl += 'The sname:VkCommandPool that pname:commandBuffer was created from'
+ paramdecl += '\n'
+
+ # Find and add any parameters that are thread unsafe
+ explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
+ if (explicitexternsyncparams is not None):
+ for param in explicitexternsyncparams:
+ externsyncattribs = param.attrib.get('externsync')
+ paramname = param.find('name')
+ for externsyncattrib in externsyncattribs.split(','):
+ paramdecl += '* '
+ paramdecl += 'Host access to '
+ if externsyncattrib == 'true':
+ if self.paramIsArray(param):
+ paramdecl += 'each member of ' + self.makeParameterName(paramname.text)
+ elif self.paramIsPointer(param):
+ paramdecl += 'the object referenced by ' + self.makeParameterName(paramname.text)
+ else:
+ paramdecl += self.makeParameterName(paramname.text)
+ else:
+ paramdecl += 'pname:'
+ paramdecl += externsyncattrib
+ paramdecl += ' must: be externally synchronized\n'
+
+ # Find and add any "implicit" parameters that are thread unsafe
+ implicitexternsyncparams = cmd.find('implicitexternsyncparams')
+ if (implicitexternsyncparams is not None):
+ for elem in implicitexternsyncparams:
+ paramdecl += '* '
+ paramdecl += 'Host access to '
+ paramdecl += elem.text
+ paramdecl += ' must: be externally synchronized\n'
+
+ if (paramdecl == ''):
+ return None
+ else:
+ return paramdecl
+
+ def makeCommandPropertiesTableEntry(self, cmd, name):
+
+ if 'vkCmd' in name:
+ # Must be called inside/outside a renderpass appropriately
+ cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
+ cmdbufferlevel = (' + \n').join(cmdbufferlevel.title().split(','))
+
+ renderpass = cmd.attrib.get('renderpass')
+ renderpass = renderpass.capitalize()
+
+ queues = cmd.attrib.get('queues')
+ queues = (' + \n').join(queues.upper().split(','))
+
+ return '|' + cmdbufferlevel + '|' + renderpass + '|' + queues
+ elif 'vkQueue' in name:
+ # Must be called inside/outside a renderpass appropriately
+
+ queues = cmd.attrib.get('queues')
+ if queues is None:
+ queues = 'Any'
+ else:
+ queues = (' + \n').join(queues.upper().split(','))
+
+ return '|-|-|' + queues
+
+ return None
+
+ def makeSuccessCodes(self, cmd, name):
+
+ successcodes = cmd.attrib.get('successcodes')
+ if successcodes is not None:
+
+ successcodeentry = ''
+ successcodes = successcodes.split(',')
+ return '* ' + '\n* '.join(successcodes)
+
+ return None
+
+ def makeErrorCodes(self, cmd, name):
+
+ errorcodes = cmd.attrib.get('errorcodes')
+ if errorcodes is not None:
+
+ errorcodeentry = ''
+ errorcodes = errorcodes.split(',')
+ return '* ' + '\n* '.join(errorcodes)
+
+ return None
+
+ #
+ # Command generation
+ def genCmd(self, cmdinfo, name):
+ OutputGenerator.genCmd(self, cmdinfo, name)
+ #
+ # Get all thh parameters
+ params = cmdinfo.elem.findall('param')
+ usages = cmdinfo.elem.findall('validity/usage')
+
+ validity = self.makeValidUsageStatements(cmdinfo.elem, name, params, usages)
+ threadsafety = self.makeThreadSafetyBlock(cmdinfo.elem, 'param')
+ commandpropertiesentry = self.makeCommandPropertiesTableEntry(cmdinfo.elem, name)
+ successcodes = self.makeSuccessCodes(cmdinfo.elem, name)
+ errorcodes = self.makeErrorCodes(cmdinfo.elem, name)
+
+ self.writeInclude('validity/protos', name, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes)
+
+ #
+ # Struct Generation
+ def genStruct(self, typeinfo, typename):
+ OutputGenerator.genStruct(self, typeinfo, typename)
+
+ # Anything that's only ever returned can't be set by the user, so shouldn't have any validity information.
+ if typeinfo.elem.attrib.get('returnedonly') is None:
+ params = typeinfo.elem.findall('member')
+ usages = typeinfo.elem.findall('validity/usage')
+
+ validity = self.makeValidUsageStatements(typeinfo.elem, typename, params, usages)
+ threadsafety = self.makeThreadSafetyBlock(typeinfo.elem, 'member')
+
+ self.writeInclude('validity/structs', typename, validity, threadsafety, None, None, None)
+ else:
+ # Still generate files for return only structs, in case this state changes later
+ self.writeInclude('validity/structs', typename, None, None, None, None, None)
+
+ #
+ # Type Generation
+ def genType(self, typeinfo, typename):
+ OutputGenerator.genType(self, typeinfo, typename)
+
+ category = typeinfo.elem.get('category')
+ if (category == 'struct' or category == 'union'):
+ self.genStruct(typeinfo, typename)
+
+# HostSynchronizationOutputGenerator - subclass of OutputGenerator.
+# Generates AsciiDoc includes of the externsync parameter table for the
+# fundamentals chapter of the Vulkan specification. Similar to
+# DocOutputGenerator.
+#
+# ---- methods ----
+# HostSynchronizationOutputGenerator(errFile, warnFile, diagFile) - args as for
+# OutputGenerator. Defines additional internal state.
+# ---- methods overriding base class ----
+# genCmd(cmdinfo)
+class HostSynchronizationOutputGenerator(OutputGenerator):
+ # Generate Host Synchronized Parameters in a table at the top of the spec
+ def __init__(self,
+ errFile = sys.stderr,
+ warnFile = sys.stderr,
+ diagFile = sys.stdout):
+ OutputGenerator.__init__(self, errFile, warnFile, diagFile)
+
+ threadsafety = {'parameters': '', 'parameterlists': '', 'implicit': ''}
+
+ def makeParameterName(self, name):
+ return 'pname:' + name
+
+ def makeFLink(self, name):
+ return 'flink:' + name
+
+ #
+ # Generate an include file
+ #
+ # directory - subdirectory to put file in
+ # basename - base name of the file
+ # contents - contents of the file (Asciidoc boilerplate aside)
+ def writeInclude(self):
+
+ if self.threadsafety['parameters'] is not None:
+ # Create file
+ filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameters.txt'
+ self.logMsg('diag', '# Generating include file:', filename)
+ fp = open(filename, 'w')
+
+ # Host Synchronization
+ write('.Externally Synchronized Parameters', file=fp)
+ write('*' * 80, file=fp)
+ write(self.threadsafety['parameters'], file=fp, end='')
+ write('*' * 80, file=fp)
+ write('', file=fp)
+
+ if self.threadsafety['parameterlists'] is not None:
+ # Create file
+ filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameterlists.txt'
+ self.logMsg('diag', '# Generating include file:', filename)
+ fp = open(filename, 'w')
+
+ # Host Synchronization
+ write('.Externally Synchronized Parameter Lists', file=fp)
+ write('*' * 80, file=fp)
+ write(self.threadsafety['parameterlists'], file=fp, end='')
+ write('*' * 80, file=fp)
+ write('', file=fp)
+
+ if self.threadsafety['implicit'] is not None:
+ # Create file
+ filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/implicit.txt'
+ self.logMsg('diag', '# Generating include file:', filename)
+ fp = open(filename, 'w')
+
+ # Host Synchronization
+ write('.Implicit Externally Synchronized Parameters', file=fp)
+ write('*' * 80, file=fp)
+ write(self.threadsafety['implicit'], file=fp, end='')
+ write('*' * 80, file=fp)
+ write('', file=fp)
+
+ fp.close()
+
+ #
+ # Check if the parameter passed in is a pointer to an array
+ def paramIsArray(self, param):
+ return param.attrib.get('len') is not None
+
+ # Check if the parameter passed in is a pointer
+ def paramIsPointer(self, param):
+ ispointer = False
+ paramtype = param.find('type')
+ if paramtype.tail is not None and '*' in paramtype.tail:
+ ispointer = True
+
+ return ispointer
+
+ # Turn the "name[].member[]" notation into plain English.
+ def makeThreadDereferenceHumanReadable(self, dereference):
+ matches = re.findall(r"[\w]+[^\w]*",dereference)
+ stringval = ''
+ for match in reversed(matches):
+ if '->' in match or '.' in match:
+ stringval += 'member of '
+ if '[]' in match:
+ stringval += 'each element of '
+
+ stringval += 'the '
+ stringval += self.makeParameterName(re.findall(r"[\w]+",match)[0])
+ stringval += ' '
+
+ stringval += 'parameter'
+
+ return stringval[0].upper() + stringval[1:]
+
+ def makeThreadSafetyBlocks(self, cmd, paramtext):
+ protoname = cmd.find('proto/name').text
+
+ # Find and add any parameters that are thread unsafe
+ explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
+ if (explicitexternsyncparams is not None):
+ for param in explicitexternsyncparams:
+ externsyncattribs = param.attrib.get('externsync')
+ paramname = param.find('name')
+ for externsyncattrib in externsyncattribs.split(','):
+
+ tempstring = '* '
+ if externsyncattrib == 'true':
+ if self.paramIsArray(param):
+ tempstring += 'Each element of the '
+ elif self.paramIsPointer(param):
+ tempstring += 'The object referenced by the '
+ else:
+ tempstring += 'The '
+
+ tempstring += self.makeParameterName(paramname.text)
+ tempstring += ' parameter'
+
+ else:
+ tempstring += self.makeThreadDereferenceHumanReadable(externsyncattrib)
+
+ tempstring += ' in '
+ tempstring += self.makeFLink(protoname)
+ tempstring += '\n'
+
+
+ if ' element of ' in tempstring:
+ self.threadsafety['parameterlists'] += tempstring
+ else:
+ self.threadsafety['parameters'] += tempstring
+
+
+ # Find and add any "implicit" parameters that are thread unsafe
+ implicitexternsyncparams = cmd.find('implicitexternsyncparams')
+ if (implicitexternsyncparams is not None):
+ for elem in implicitexternsyncparams:
+ self.threadsafety['implicit'] += '* '
+ self.threadsafety['implicit'] += elem.text[0].upper()
+ self.threadsafety['implicit'] += elem.text[1:]
+ self.threadsafety['implicit'] += ' in '
+ self.threadsafety['implicit'] += self.makeFLink(protoname)
+ self.threadsafety['implicit'] += '\n'
+
+
+ # For any vkCmd* functions, the commandBuffer parameter must be being recorded
+ if protoname is not None and 'vkCmd' in protoname:
+ self.threadsafety['implicit'] += '* '
+ self.threadsafety['implicit'] += 'The sname:VkCommandPool that pname:commandBuffer was allocated from, in '
+ self.threadsafety['implicit'] += self.makeFLink(protoname)
+
+ self.threadsafety['implicit'] += '\n'
+
+ #
+ # Command generation
+ def genCmd(self, cmdinfo, name):
+ OutputGenerator.genCmd(self, cmdinfo, name)
+ #
+ # Get all thh parameters
+ params = cmdinfo.elem.findall('param')
+ usages = cmdinfo.elem.findall('validity/usage')
+
+ self.makeThreadSafetyBlocks(cmdinfo.elem, 'param')
+
+ self.writeInclude()