diff --git a/src/gfxstream/codegen/scripts/cereal/common/vulkantypes.py b/src/gfxstream/codegen/scripts/cereal/common/vulkantypes.py index 8f2fff2c72f..79d7dad85b8 100644 --- a/src/gfxstream/codegen/scripts/cereal/common/vulkantypes.py +++ b/src/gfxstream/codegen/scripts/cereal/common/vulkantypes.py @@ -661,15 +661,12 @@ def parse_func_expr(parsed_sexp): def parseFilterFuncExpr(expr): res = parse_func_expr(parse_sexp(expr)) - print("parseFilterFuncExpr: parsed %s" % res) return res def parseLetBodyExpr(expr): res = parse_func_expr(parse_sexp(expr)) - print("parseLetBodyExpr: parsed %s" % res) return res - def makeVulkanTypeFromXMLTag(typeInfo, parentName: str, tag: Element) -> VulkanType: res = VulkanType() diff --git a/src/gfxstream/codegen/scripts/cereal/counting.py b/src/gfxstream/codegen/scripts/cereal/counting.py index b6d628aad31..8df2570f94d 100644 --- a/src/gfxstream/codegen/scripts/cereal/counting.py +++ b/src/gfxstream/codegen/scripts/cereal/counting.py @@ -418,9 +418,6 @@ class VulkanCountingCodegen(VulkanTypeIterator): self.beginFilterGuard(vulkanType) self.doAllocSpace(vulkanType) - if vulkanType.filterVar != None: - print("onPointer Needs filter: %s filterVar %s" % (access, vulkanType.filterVar)) - if vulkanType.isHandleType() and self.mapHandles: self.genHandleMappingCall(vulkanType, access, lenAccess) else: @@ -457,8 +454,6 @@ class VulkanCountingCodegen(VulkanTypeIterator): if vulkanType.isHandleType() and self.mapHandles: access = self.exprAccessor(vulkanType) - if vulkanType.filterVar != None: - print("onValue Needs filter: %s filterVar %s" % (access, vulkanType.filterVar)) self.genHandleMappingCall( vulkanType.getForAddressAccess(), access, "1") elif self.typeInfo.isNonAbiPortableType(vulkanType.typeName): diff --git a/src/gfxstream/codegen/scripts/cereal/marshaling.py b/src/gfxstream/codegen/scripts/cereal/marshaling.py index 0593fb3ceb6..a7d6394611d 100644 --- a/src/gfxstream/codegen/scripts/cereal/marshaling.py +++ b/src/gfxstream/codegen/scripts/cereal/marshaling.py @@ -540,9 +540,6 @@ class VulkanMarshalingCodegen(VulkanTypeIterator): self.beginFilterGuard(vulkanType) self.doAllocSpace(vulkanType) - if vulkanType.filterVar != None: - print("onPointer Needs filter: %s filterVar %s" % (access, vulkanType.filterVar)) - if vulkanType.isHandleType() and self.mapHandles: self.genHandleMappingCall(vulkanType, access, lenAccess) else: @@ -576,8 +573,6 @@ class VulkanMarshalingCodegen(VulkanTypeIterator): if vulkanType.isHandleType() and self.mapHandles: access = self.exprAccessor(vulkanType) - if vulkanType.filterVar != None: - print("onValue Needs filter: %s filterVar %s" % (access, vulkanType.filterVar)) self.genHandleMappingCall( vulkanType.getForAddressAccess(), access, "1") elif self.typeInfo.isNonAbiPortableType(vulkanType.typeName): diff --git a/src/gfxstream/codegen/scripts/cereal/reservedmarshaling.py b/src/gfxstream/codegen/scripts/cereal/reservedmarshaling.py index 492077e42fc..4f15663c98a 100644 --- a/src/gfxstream/codegen/scripts/cereal/reservedmarshaling.py +++ b/src/gfxstream/codegen/scripts/cereal/reservedmarshaling.py @@ -638,9 +638,6 @@ class VulkanReservedMarshalingCodegen(VulkanTypeIterator): self.beginFilterGuard(vulkanType) self.doAllocSpace(vulkanType) - if vulkanType.filterVar != None: - print("onPointer Needs filter: %s filterVar %s" % (access, vulkanType.filterVar)) - if vulkanType.isHandleType() and self.mapHandles: self.genHandleMappingCall( vulkanType, access, lenAccess, lenAccessGuard) @@ -675,8 +672,6 @@ class VulkanReservedMarshalingCodegen(VulkanTypeIterator): if vulkanType.isHandleType() and self.mapHandles: access = self.exprAccessor(vulkanType) - if vulkanType.filterVar != None: - print("onValue Needs filter: %s filterVar %s" % (access, vulkanType.filterVar)) self.genHandleMappingCall( vulkanType.getForAddressAccess(), access, "1", None) elif self.typeInfo.isNonAbiPortableType(vulkanType.typeName): diff --git a/src/gfxstream/codegen/scripts/cerealgenerator.py b/src/gfxstream/codegen/scripts/cerealgenerator.py index 7d650ff06be..8a3eadcfdeb 100644 --- a/src/gfxstream/codegen/scripts/cerealgenerator.py +++ b/src/gfxstream/codegen/scripts/cerealgenerator.py @@ -241,7 +241,6 @@ def banner_command(argv): def envGetOrDefault(key, default=None): if key in os.environ: return os.environ[key] - print("envGetOrDefault: notfound: %s" % key) return default # ---- methods overriding base class ---- diff --git a/src/gfxstream/codegen/scripts/cgenerator.py b/src/gfxstream/codegen/scripts/cgenerator.py index ef8d68137c0..44a2ba4c7f0 100644 --- a/src/gfxstream/codegen/scripts/cgenerator.py +++ b/src/gfxstream/codegen/scripts/cgenerator.py @@ -1,6 +1,7 @@ #!/usr/bin/python3 -i # # Copyright 2013-2023 The Khronos Group Inc. +# Copyright 2023-2024 Google Inc. # # SPDX-License-Identifier: Apache-2.0 @@ -8,7 +9,6 @@ import os import re from generator import (GeneratorOptions, - MissingGeneratorOptionsConventionsError, MissingGeneratorOptionsError, MissingRegistryError, OutputGenerator, noneStr, regSortFeatures, write) @@ -20,25 +20,9 @@ class CGeneratorOptions(GeneratorOptions): def __init__(self, prefixText='', - genFuncPointers=True, - protectFile=True, - protectFeature=True, - protectProto=None, - protectProtoStr=None, - protectExtensionProto=None, - protectExtensionProtoStr=None, - apicall='', apientry='', apientryp='', - indentFuncProto=True, - indentFuncPointer=False, alignFuncParam=0, - genEnumBeginEndRange=False, - genAliasMacro=False, - genStructExtendsComment=False, - aliasMacro='', - misracstyle=False, - misracppstyle=False, **kwargs ): """Constructor. @@ -46,115 +30,27 @@ class CGeneratorOptions(GeneratorOptions): - 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 - - protectExtensionProto - If conditional protection should be generated - around extension prototype declarations, set to either '#ifdef' - to require opt-in (#ifdef protectExtensionProtoStr) or '#ifndef' - to require opt-out (#ifndef protectExtensionProtoStr). Otherwise - set to None - - protectExtensionProtoStr - #ifdef/#ifndef symbol to use around - extension prototype declarations, if protectExtensionProto 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 - - genEnumBeginEndRange - True if BEGIN_RANGE / END_RANGE macros should - be generated for enumerated types - - genAliasMacro - True if the OpenXR alias macro should be generated - for aliased types (unclear what other circumstances this is useful) - - genStructExtendsComment - True if comments showing the structures - whose pNext chain a structure extends are included before its - definition - - aliasMacro - alias macro to inject when genAliasMacro is True - - misracstyle - generate MISRA C-friendly headers - - misracppstyle - generate MISRA C++-friendly headers""" + separate line, align parameter names at the specified column""" GeneratorOptions.__init__(self, **kwargs) self.prefixText = prefixText """list of strings to prefix generated header with (usually a copyright statement + calling convention macros).""" - self.genFuncPointers = genFuncPointers - """True if function pointer typedefs should be generated""" - - self.protectFile = protectFile - """True if multiple inclusion protection should be generated (based on the filename) around the entire header.""" - - self.protectFeature = protectFeature - """True if #ifndef..#endif protection should be generated around a feature interface in the header file.""" - - self.protectProto = 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.""" - - self.protectProtoStr = protectProtoStr - """#ifdef/#ifndef symbol to use around prototype declarations, if protectProto is set""" - - self.protectExtensionProto = protectExtensionProto - """If conditional protection should be generated around extension prototype declarations, set to either '#ifdef' to require opt-in (#ifdef protectExtensionProtoStr) or '#ifndef' to require opt-out (#ifndef protectExtensionProtoStr). Otherwise set to None.""" - - self.protectExtensionProtoStr = protectExtensionProtoStr - """#ifdef/#ifndef symbol to use around extension prototype declarations, if protectExtensionProto is set""" - - self.apicall = apicall - """string to use for the function declaration prefix, such as APICALL on Windows.""" - self.apientry = apientry """string to use for the calling convention macro, in typedefs, such as APIENTRY.""" self.apientryp = apientryp """string to use for the calling convention macro in function pointer typedefs, such as APIENTRYP.""" - self.indentFuncProto = indentFuncProto - """True if prototype declarations should put each parameter on a separate line""" - - self.indentFuncPointer = indentFuncPointer - """True if typedefed function pointers should put each parameter on a separate line""" - self.alignFuncParam = alignFuncParam """if nonzero and parameters are being put on a separate line, align parameter names at the specified column""" - self.genEnumBeginEndRange = genEnumBeginEndRange - """True if BEGIN_RANGE / END_RANGE macros should be generated for enumerated types""" - - self.genAliasMacro = genAliasMacro - """True if the OpenXR alias macro should be generated for aliased types (unclear what other circumstances this is useful)""" - - self.genStructExtendsComment = genStructExtendsComment - """True if comments showing the structures whose pNext chain a structure extends are included before its definition""" - - self.aliasMacro = aliasMacro - """alias macro to inject when genAliasMacro is True""" - - self.misracstyle = misracstyle - """generate MISRA C-friendly headers""" - - self.misracppstyle = misracppstyle - """generate MISRA C++-friendly headers""" - - self.codeGenerator = True - """True if this generator makes compilable code""" - - class COutputGenerator(OutputGenerator): """Generates C-language API interfaces.""" @@ -168,21 +64,11 @@ class COutputGenerator(OutputGenerator): # Internal state - accumulators for different inner block text self.sections = {section: [] for section in self.ALL_SECTIONS} self.feature_not_empty = False - self.may_alias = None def beginFile(self, genOpts): OutputGenerator.beginFile(self, genOpts) if self.genOpts is None: raise MissingGeneratorOptionsError() - # C-specific - # - # Multiple inclusion protection & C++ wrappers. - if self.genOpts.protectFile and self.genOpts.filename: - headerSym = re.sub(r'\.h', '_h_', - os.path.basename(self.genOpts.filename)).upper() - write('#ifndef', headerSym, file=self.outFile) - write('#define', headerSym, '1', file=self.outFile) - self.newline() # User-supplied prefix text, if any (list of strings) if genOpts.prefixText: @@ -205,9 +91,6 @@ class COutputGenerator(OutputGenerator): 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) @@ -221,18 +104,6 @@ class COutputGenerator(OutputGenerator): self.sections = {section: [] for section in self.ALL_SECTIONS} self.feature_not_empty = False - def _endProtectComment(self, protect_str, protect_directive='#ifdef'): - if protect_directive is None or protect_str is None: - raise RuntimeError('Should not call in here without something to protect') - - # Do not put comments after #endif closing blocks if this is not set - if not self.genOpts.conventions.protectProtoComment: - return '' - elif 'ifdef' in protect_directive: - return f' /* {protect_str} */' - else: - return f' /* !{protect_str} */' - def endFeature(self): "Actually write the interface to the output file." # C-specific @@ -240,63 +111,24 @@ class COutputGenerator(OutputGenerator): if self.feature_not_empty: if self.genOpts is None: raise MissingGeneratorOptionsError() - if self.genOpts.conventions is None: - raise MissingGeneratorOptionsConventionsError() - is_core = self.featureName and self.featureName.startswith(self.conventions.api_prefix + 'VERSION_') - if self.genOpts.conventions.writeFeature(self.featureExtraProtect, self.genOpts.filename): - self.newline() - if self.genOpts.protectFeature: - write('#ifndef', self.featureName, file=self.outFile) + is_core = self.featureName and self.featureName.startswith('VK_VERSION_') + self.newline() - # 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 is not None: - write('#ifdef', self.featureExtraProtect, file=self.outFile) + # Generate warning of possible use in IDEs + write(f'// {self.featureName} is a preprocessor guard. Do not pass it to API calls.', 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) + + if self.sections['commandPointer']: + write('\n'.join(self.sections['commandPointer']), file=self.outFile) self.newline() - # Generate warning of possible use in IDEs - write(f'// {self.featureName} is a preprocessor guard. Do not pass it to API calls.', 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) + if self.sections['command']: + write('\n'.join(self.sections['command']), end='', file=self.outFile) - 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) - if self.genOpts.protectExtensionProto and not is_core: - write(self.genOpts.protectExtensionProto, - self.genOpts.protectExtensionProtoStr, file=self.outFile) - write('\n'.join(self.sections['command']), end='', file=self.outFile) - if self.genOpts.protectExtensionProto and not is_core: - write('#endif' + - self._endProtectComment(protect_directive=self.genOpts.protectExtensionProto, - protect_str=self.genOpts.protectExtensionProtoStr), - file=self.outFile) - if self.genOpts.protectProto: - write('#endif' + - self._endProtectComment(protect_directive=self.genOpts.protectProto, - protect_str=self.genOpts.protectProtoStr), - file=self.outFile) - else: - self.newline() - - if self.featureExtraProtect is not None: - write('#endif' + - self._endProtectComment(protect_str=self.featureExtraProtect), - file=self.outFile) - - if self.genOpts.protectFeature: - write('#endif' + - self._endProtectComment(protect_str=self.featureName), - file=self.outFile) # Finish processing in superclass OutputGenerator.endFeature(self) @@ -304,7 +136,6 @@ class COutputGenerator(OutputGenerator): "Append a definition to the specified section" if section is None: - self.logMsg('error', 'Missing section in appendSection (probably a element missing its \'category\' attribute. Text:', text) exit(1) self.sections[section].append(text) @@ -334,22 +165,15 @@ class COutputGenerator(OutputGenerator): else: if self.genOpts is None: raise MissingGeneratorOptionsError() - # OpenXR: this section was not under 'else:' previously, just fell through - if alias: - # If the type is an alias, just emit a typedef declaration - body = 'typedef ' + alias + ' ' + name + ';\n' - else: - # Replace tags with an APIENTRY-style string - # (from self.genOpts). Copy other text through unchanged. - # If the resulting text is an empty string, do not emit it. - body = noneStr(typeElem.text) - for elem in typeElem: - if elem.tag == 'apientry': - body += self.genOpts.apientry + noneStr(elem.tail) - else: - body += noneStr(elem.text) + noneStr(elem.tail) - if category == 'define' and self.misracppstyle(): - body = body.replace("(uint32_t)", "static_cast") + # Replace tags with an APIENTRY-style string + # (from self.genOpts). Copy other text through unchanged. + # If the resulting text is an empty string, do not emit it. + body = noneStr(typeElem.text) + for elem in typeElem: + if elem.tag == 'apientry': + body += self.genOpts.apientry + noneStr(elem.tail) + else: + body += noneStr(elem.text) + noneStr(elem.tail) if body: # Add extra newline after multi-line entries. if '\n' in body[0:-1]: @@ -380,25 +204,6 @@ class COutputGenerator(OutputGenerator): return (protect_if_str, protect_end_str) - def typeMayAlias(self, typeName): - if not self.may_alias: - if self.registry is None: - raise MissingRegistryError() - # First time we have asked if a type may alias. - # So, populate the set of all names of types that may. - - # Everyone with an explicit mayalias="true" - self.may_alias = set(typeName - for typeName, data in self.registry.typedict.items() - if data.elem.get('mayalias') == 'true') - - # Every type mentioned in some other type's parentstruct attribute. - polymorphic_bases = (otherType.elem.get('parentstruct') - for otherType in self.registry.typedict.values()) - self.may_alias.update(set(x for x in polymorphic_bases - if x is not None)) - return typeName in self.may_alias - def genStruct(self, typeinfo, typeName, alias): """Generate struct (e.g. C "struct" type). @@ -426,18 +231,8 @@ class COutputGenerator(OutputGenerator): if protect_begin: body += protect_begin - if self.genOpts.genStructExtendsComment: - structextends = typeElem.get('structextends') - body += '// ' + typeName + ' extends ' + structextends + '\n' if structextends else '' - body += 'typedef ' + typeElem.get('category') - # This is an OpenXR-specific alternative where aliasing refers - # to an inheritance hierarchy of types rather than C-level type - # aliases. - if self.genOpts.genAliasMacro and self.typeMayAlias(typeName): - body += ' ' + self.genOpts.aliasMacro - body += ' ' + typeName + ' {\n' targetLen = self.getMaxCParamTypeLength(typeinfo) @@ -472,11 +267,6 @@ class COutputGenerator(OutputGenerator): # for the alias. body = 'typedef ' + alias + ' ' + groupName + ';\n' self.appendSection(section, body) - else: - if self.genOpts is None: - raise MissingGeneratorOptionsError() - (section, body) = self.buildEnumCDecl(self.genOpts.genEnumBeginEndRange, groupinfo, groupName) - self.appendSection(section, '\n' + body) def genEnum(self, enuminfo, name, alias): """Generate the C declaration for a constant (a single value). @@ -493,21 +283,10 @@ class COutputGenerator(OutputGenerator): "Command generation" OutputGenerator.genCmd(self, cmdinfo, name, alias) - # if alias: - # prefix = '// ' + name + ' is an alias of command ' + alias + '\n' - # else: - # prefix = '' if self.genOpts is None: raise MissingGeneratorOptionsError() prefix = '' decls = self.makeCDecls(cmdinfo.elem) self.appendSection('command', prefix + decls[0] + '\n') - if self.genOpts.genFuncPointers: - self.appendSection('commandPointer', decls[1]) - - def misracstyle(self): - return self.genOpts.misracstyle; - - def misracppstyle(self): - return self.genOpts.misracppstyle; + self.appendSection('commandPointer', decls[1]) diff --git a/src/gfxstream/codegen/scripts/generator.py b/src/gfxstream/codegen/scripts/generator.py index 107cffa7d50..51ae8cf1cbb 100644 --- a/src/gfxstream/codegen/scripts/generator.py +++ b/src/gfxstream/codegen/scripts/generator.py @@ -1,6 +1,7 @@ #!/usr/bin/python3 -i # # Copyright 2013-2023 The Khronos Group Inc. +# Copyright 2023-2024 Google Inc. # # SPDX-License-Identifier: Apache-2.0 """Base class for source/header/doc generators, as well as some utility functions.""" @@ -9,7 +10,6 @@ from __future__ import unicode_literals import io import os -import pdb import re import shutil import sys @@ -19,8 +19,40 @@ try: except ImportError: from pathlib2 import Path # type: ignore -from spec_tools.util import getElemName, getElemType +CATEGORIES_REQUIRING_VALIDATION = set(('handle', + 'enum', + 'bitmask', + 'basetype', + None)) +# These are basic C types pulled in via openxr_platform_defines.h +TYPES_KNOWN_ALWAYS_VALID = set(('char', + 'float', + 'int8_t', 'uint8_t', + 'int16_t', 'uint16_t', + 'int32_t', 'uint32_t', + 'int64_t', 'uint64_t', + 'size_t', + 'intptr_t', 'uintptr_t', + 'int', + )) + +def getElemName(elem, default=None): + """Get the name associated with an element, either a name child or name attribute.""" + name_elem = elem.find('name') + if name_elem is not None: + return name_elem.text + # Fallback if there is no child. + return elem.get('name', default) + + +def getElemType(elem, default=None): + """Get the type associated with an element, either a type child or type attribute.""" + type_elem = elem.find('type') + if type_elem is not None: + return type_elem.text + # Fallback if there is no child. + return elem.get('type', default) def write(*args, **kwargs): file = kwargs.pop('file', sys.stdout) @@ -28,6 +60,23 @@ def write(*args, **kwargs): file.write(' '.join(str(arg) for arg in args)) file.write(end) +def category_requires_validation(category): + """Return True if the given type 'category' always requires validation. + + Defaults to a reasonable implementation. + + May override.""" + return category in CATEGORIES_REQUIRING_VALIDATION + +def type_always_valid(typename): + """Return True if the given type name is always valid (never requires validation). + + This is for things like integers. + + Defaults to a reasonable implementation. + + May override.""" + return typename in TYPES_KNOWN_ALWAYS_VALID def noneStr(s): """Return string argument, or "" if argument is None. @@ -38,18 +87,6 @@ def noneStr(s): return s return "" - -def enquote(s): - """Return string argument with surrounding quotes, - for serialization into Python code.""" - if s: - if isinstance(s, str): - return f"'{s}'" - else: - return s - return None - - def regSortCategoryKey(feature): """Sort key for regSortFeatures. Sorts by category of the feature name string: @@ -130,17 +167,6 @@ class MissingRegistryError(RuntimeError): full_msg += ': ' + msg super().__init__(full_msg) - -class MissingGeneratorOptionsConventionsError(RuntimeError): - """Error raised when a Generator tries to do something that requires a Conventions object but it is None.""" - - def __init__(self, msg=None): - full_msg = 'Missing Conventions object self.genOpts.conventions' - if msg: - full_msg += ': ' + msg - super().__init__(full_msg) - - class GeneratorOptions: """Base class for options used during header/documentation production. @@ -148,71 +174,34 @@ class GeneratorOptions: Registry.apiGen() and by base OutputGenerator objects.""" def __init__(self, - conventions=None, filename=None, directory='.', - genpath=None, - apiname=None, - mergeApiNames=None, - profile=None, versions='.*', emitversions='.*', - defaultExtensions=None, addExtensions=None, - removeExtensions=None, emitExtensions=None, - emitSpirv=None, - emitFormats=None, - reparentEnums=True, sortProcedure=regSortFeatures, - requireCommandAliases=False, - requireDepends=True, ): """Constructor. Arguments: - - conventions - may be mandatory for some generators: an object that implements ConventionsBase - filename - basename of file to generate, or None to write to stdout. - directory - directory in which to generate filename - - genpath - path to previously generated files, such as apimap.py - - apiname - string matching `` 'apiname' attribute, e.g. 'gl'. - - mergeApiNames - If not None, a comma separated list of API names - to merge into the API specified by 'apiname' - - 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 ``. 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. - emitExtensions - regex matching names of extensions to actually emit interfaces for (though all requested versions are considered when deciding which interfaces to generate). Defaults to None. - - emitSpirv - regex matching names of extensions and capabilities - to actually emit interfaces for. - - emitFormats - regex matching names of formats to actually emit - interfaces for. - - reparentEnums - move elements which extend an enumerated - type from or elements to the target - element. This is required for almost all purposes, but the - InterfaceGenerator relies on the list of interfaces in the - or being complete. Defaults to True. - sortProcedure - takes a list of FeatureInfo objects and sorts them in place to a preferred order in the generated output. - - requireCommandAliases - if True, treat command aliases - as required dependencies. - - requireDepends - whether to follow API dependencies when emitting - APIs. Default is - core API versions @@ -222,28 +211,13 @@ class GeneratorOptions: The regex patterns can be None or empty, in which case they match nothing.""" - self.conventions = conventions - """may be mandatory for some generators: - an object that implements ConventionsBase""" self.filename = filename "basename of file to generate, or None to write to stdout." - self.genpath = genpath - """path to previously generated files, such as apimap.py""" - self.directory = directory "directory in which to generate filename" - self.apiname = apiname - "string matching `` 'apiname' attribute, e.g. 'gl'." - - self.mergeApiNames = mergeApiNames - "comma separated list of API names to merge into the API specified by 'apiname'" - - self.profile = profile - "string specifying API profile , e.g. 'core', or None." - self.versions = self.emptyRegex(versions) """regex matching API versions to process interfaces for. Normally `'.*'` or `'[0-9][.][0-9]'` to match all defined versions.""" @@ -254,56 +228,24 @@ class GeneratorOptions: when deciding which interfaces to generate). For GL 4.3 glext.h, this might be `'1[.][2-5]|[2-4][.][0-9]'`.""" - self.defaultExtensions = defaultExtensions - """If not None, a string which must in its - entirety match the pattern in the "supported" attribute of - the ``. Defaults to None. Usually the same as apiname.""" - self.addExtensions = self.emptyRegex(addExtensions) """regex matching names of additional extensions to include. Defaults to None.""" - self.removeExtensions = self.emptyRegex(removeExtensions) - """regex matching names of extensions to - remove (after defaultExtensions and addExtensions). Defaults - to None.""" - self.emitExtensions = self.emptyRegex(emitExtensions) """regex matching names of extensions to actually emit interfaces for (though all requested versions are considered when deciding which interfaces to generate).""" - self.emitSpirv = self.emptyRegex(emitSpirv) - """regex matching names of extensions and capabilities - to actually emit interfaces for.""" - - self.emitFormats = self.emptyRegex(emitFormats) - """regex matching names of formats - to actually emit interfaces for.""" - - self.reparentEnums = reparentEnums - """boolean specifying whether to remove elements from - or when extending an type.""" - self.sortProcedure = 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.""" - self.codeGenerator = False - """True if this generator makes compilable code""" - self.registry = None """Populated later with the registry object.""" - self.requireCommandAliases = requireCommandAliases - """True if alias= attributes of tags are transitively - required.""" - - self.requireDepends = requireDepends - """True if dependencies of API tags are transitively required.""" - def emptyRegex(self, pat): """Substitute a regular expression which matches no version or extension names for None or the empty string.""" @@ -331,17 +273,6 @@ class OutputGenerator: 'basetype': 'basetypes', } - def breakName(self, name, msg): - """Break into debugger if this is a special name""" - - # List of string names to break on - bad = ( - ) - - if name in bad and True: - print('breakName {}: {}'.format(name, msg)) - pdb.set_trace() - def __init__(self, errFile=sys.stderr, warnFile=sys.stderr, diagFile=sys.stdout): """Constructor @@ -376,35 +307,6 @@ class OutputGenerator: # derived generators. self.apidict = None - # File suffix for generated files, set in beginFile below. - self.file_suffix = '' - - def logMsg(self, level, *args): - """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""" - if level == 'error': - strfile = io.StringIO() - write('ERROR:', *args, file=strfile) - if self.errFile is not None: - write(strfile.getvalue(), file=self.errFile) - raise UserWarning(strfile.getvalue()) - elif level == 'warn': - if self.warnFile is not None: - write('WARNING:', *args, file=self.warnFile) - elif level == 'diag': - if self.diagFile is not None: - write('DIAG:', *args, file=self.diagFile) - else: - raise UserWarning( - '*** FATAL ERROR in Generator.logMsg: unknown level:' + level) - def enumToValue(self, elem, needsNum, bitwidth = 32, forceSuffix = False, parent_for_alias_dereference=None): """Parse and convert an `` tag into a value. @@ -439,8 +341,6 @@ class OutputGenerator: declared first when emitting this enum.""" if self.genOpts is None: raise MissingGeneratorOptionsError() - if self.genOpts.conventions is None: - raise MissingGeneratorOptionsConventionsError() name = elem.get('name') numVal = None @@ -459,7 +359,6 @@ class OutputGenerator: value = value + 'ULL' else: value = value + 'U' - self.logMsg('diag', 'Enum', name, '-> value [', numVal, ',', value, ']') return [numVal, value] if 'bitpos' in elem.keys(): value = elem.get('bitpos') @@ -470,7 +369,6 @@ class OutputGenerator: value = value + 'ULL' elif forceSuffix: value = value + 'U' - self.logMsg('diag', 'Enum', name, '-> bitpos [', numVal, ',', value, ']') return [numVal, value] if 'offset' in elem.keys(): # Obtain values in the mapping from the attributes @@ -480,9 +378,6 @@ class OutputGenerator: 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 @@ -490,7 +385,6 @@ class OutputGenerator: numVal *= -1 value = '%d' % numVal # More logic needed! - self.logMsg('diag', 'Enum', name, '-> offset [', numVal, ',', value, ']') return [numVal, value] if 'alias' in elem.keys(): alias_of = elem.get('alias') @@ -504,321 +398,6 @@ class OutputGenerator: raise RuntimeError("Could not find the aliased enum value") return [None, None] - def checkDuplicateEnums(self, enums): - """Check enumerated values for duplicates. - - - enums - list of `` Elements - - returns the list with duplicates stripped""" - # Dictionaries indexed by name and numeric value. - # Entries are [ Element, numVal, strVal ] matching name or value - - nameMap = {} - valueMap = {} - - stripped = [] - for elem in enums: - name = elem.get('name') - (numVal, strVal) = self.enumToValue(elem, True) - - if name in nameMap: - # Duplicate name found; check values - (name2, numVal2, strVal2) = nameMap[name] - - # Duplicate enum values for the same name are benign. This - # happens when defining the same enum conditionally in - # several extension blocks. - if (strVal2 == strVal or (numVal is not None - and numVal == numVal2)): - True - # self.logMsg('info', 'checkDuplicateEnums: Duplicate enum (' + name + - # ') found with the same value:' + strVal) - else: - self.logMsg('warn', 'checkDuplicateEnums: Duplicate enum (' + name - + ') found with different values:' + strVal - + ' and ' + strVal2) - - # Do not add the duplicate to the returned list - continue - elif numVal in valueMap: - # Duplicate value found (such as an alias); report it, but - # still add this enum to the list. - (name2, numVal2, strVal2) = valueMap[numVal] - - msg = 'Two enums found with the same value: {} = {} = {}'.format( - name, name2.get('name'), strVal) - self.logMsg('error', msg) - - # Track this enum to detect followon duplicates - nameMap[name] = [elem, numVal, strVal] - if numVal is not None: - valueMap[numVal] = [elem, numVal, strVal] - - # Add this enum to the list - stripped.append(elem) - - # Return the list - return stripped - - def misracstyle(self): - return False; - - def misracppstyle(self): - return False; - - def buildEnumCDecl(self, expand, groupinfo, groupName): - """Generate the C declaration for an enum""" - if self.genOpts is None: - raise MissingGeneratorOptionsError() - if self.genOpts.conventions is None: - raise MissingGeneratorOptionsConventionsError() - - groupElem = groupinfo.elem - - # Determine the required bit width for the enum group. - # 32 is the default, which generates C enum types for the values. - bitwidth = 32 - - # If the constFlagBits preference is set, 64 is the default for bitmasks - if self.genOpts.conventions.constFlagBits and groupElem.get('type') == 'bitmask': - bitwidth = 64 - - # Check for an explicitly defined bitwidth, which will override any defaults. - if groupElem.get('bitwidth'): - try: - bitwidth = int(groupElem.get('bitwidth')) - except ValueError as ve: - self.logMsg('error', 'Invalid value for bitwidth attribute (', groupElem.get('bitwidth'), ') for ', groupName, ' - must be an integer value\n') - exit(1) - - usebitmask = False - usedefine = False - - # Bitmask flags can be generated as either "static const uint{32,64}_t" values, - # or as 32-bit C enums. 64-bit types must use uint64_t values. - if groupElem.get('type') == 'bitmask': - if bitwidth > 32 or self.misracppstyle(): - usebitmask = True - if self.misracstyle(): - usedefine = True - - if usedefine or usebitmask: - # Validate the bitwidth and generate values appropriately - if bitwidth > 64: - self.logMsg('error', 'Invalid value for bitwidth attribute (', groupElem.get('bitwidth'), ') for bitmask type ', groupName, ' - must be less than or equal to 64\n') - exit(1) - else: - return self.buildEnumCDecl_BitmaskOrDefine(groupinfo, groupName, bitwidth, usedefine) - else: - # Validate the bitwidth and generate values appropriately - if bitwidth > 32: - self.logMsg('error', 'Invalid value for bitwidth attribute (', groupElem.get('bitwidth'), ') for enum type ', groupName, ' - must be less than or equal to 32\n') - exit(1) - else: - return self.buildEnumCDecl_Enum(expand, groupinfo, groupName) - - def buildEnumCDecl_BitmaskOrDefine(self, groupinfo, groupName, bitwidth, usedefine): - """Generate the C declaration for an "enum" that is actually a - set of flag bits""" - groupElem = groupinfo.elem - flagTypeName = groupElem.get('name') - - # Prefix - body = "// Flag bits for " + flagTypeName + "\n" - - if bitwidth == 64: - body += "typedef VkFlags64 %s;\n" % flagTypeName; - else: - body += "typedef VkFlags %s;\n" % flagTypeName; - - # Maximum allowable value for a flag (unsigned 64-bit integer) - maxValidValue = 2**(64) - 1 - minValidValue = 0 - - # Get a list of nested 'enum' tags. - enums = groupElem.findall('enum') - - # Check for and report duplicates, and return a list with them - # removed. - enums = self.checkDuplicateEnums(enums) - - # Accumulate non-numeric enumerant values separately and append - # them following the numeric values, to allow for aliases. - # NOTE: this does not do a topological sort yet, so aliases of - # aliases can still get in the wrong order. - aliasText = '' - - # Loop over the nested 'enum' tags. - for elem in enums: - # 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, bitwidth, True) - name = elem.get('name') - - # Range check for the enum value - if numVal is not None and (numVal > maxValidValue or numVal < minValidValue): - self.logMsg('error', 'Allowable range for flag types in C is [', minValidValue, ',', maxValidValue, '], but', name, 'flag has a value outside of this (', strVal, ')\n') - exit(1) - - decl = self.genRequirements(name, mustBeFound = False) - - if self.isEnumRequired(elem): - protect = elem.get('protect') - if protect is not None: - body += '#ifdef {}\n'.format(protect) - - if usedefine: - decl += "#define {} {}\n".format(name, strVal) - elif self.misracppstyle(): - decl += "static constexpr {} {} {{{}}};\n".format(flagTypeName, name, strVal) - else: - # Some C compilers only allow initializing a 'static const' variable with a literal value. - # So initializing an alias from another 'static const' value would fail to compile. - # Work around this by chasing the aliases to get the actual value. - while numVal is None: - alias = self.registry.tree.find("enums/enum[@name='" + strVal + "']") - if alias is not None: - (numVal, strVal) = self.enumToValue(alias, True, bitwidth, True) - else: - self.logMsg('error', 'No such alias {} for enum {}'.format(strVal, name)) - decl += "static const {} {} = {};\n".format(flagTypeName, name, strVal) - - if numVal is not None: - body += decl - else: - aliasText += decl - - if protect is not None: - body += '#endif\n' - - # Now append the non-numeric enumerant values - body += aliasText - - # Postfix - - return ("bitmask", body) - - def buildEnumCDecl_Enum(self, expand, groupinfo, groupName): - """Generate the C declaration for an enumerated type""" - groupElem = groupinfo.elem - - # Break the group name into prefix and suffix portions for range - # enum generation - expandName = re.sub(r'([0-9]+|[a-z_])([A-Z0-9])', r'\1_\2', groupName).upper() - expandPrefix = expandName - expandSuffix = '' - expandSuffixMatch = re.search(r'[A-Z][A-Z]+$', groupName) - if expandSuffixMatch: - expandSuffix = '_' + expandSuffixMatch.group() - # Strip off the suffix from the prefix - expandPrefix = expandName.rsplit(expandSuffix, 1)[0] - - # Prefix - body = ["typedef enum %s {" % groupName] - - # @@ Should use the type="bitmask" attribute instead - isEnum = ('FLAG_BITS' not in expandPrefix) - - # Allowable range for a C enum - which is that of a signed 32-bit integer - maxValidValue = 2**(32 - 1) - 1 - minValidValue = (maxValidValue * -1) - 1 - - # Get a list of nested 'enum' tags. - enums = groupElem.findall('enum') - - # Check for and report duplicates, and return a list with them - # removed. - enums = self.checkDuplicateEnums(enums) - - # 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 - - # Accumulate non-numeric enumerant values separately and append - # them following the numeric values, to allow for aliases. - # NOTE: this does not do a topological sort yet, so aliases of - # aliases can still get in the wrong order. - aliasText = [] - - maxName = None - minValue = None - maxValue = None - for elem in enums: - # 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') - - # Extension enumerants are only included if they are required - if self.isEnumRequired(elem): - decl = '' - - protect = elem.get('protect') - if protect is not None: - decl += '#ifdef {}\n'.format(protect) - - # Indent requirements comment, if there is one - requirements = self.genRequirements(name, mustBeFound = False) - if requirements != '': - requirements = ' ' + requirements - decl += requirements - decl += ' {} = {},'.format(name, strVal) - - if protect is not None: - decl += '\n#endif' - - if numVal is not None: - body.append(decl) - else: - aliasText.append(decl) - - # Range check for the enum value - if numVal is not None and (numVal > maxValidValue or numVal < minValidValue): - self.logMsg('error', 'Allowable range for C enum types is [', minValidValue, ',', maxValidValue, '], but', name, 'has a value outside of this (', strVal, ')\n') - exit(1) - - # Do not track min/max for non-numbers (numVal is None) - if isEnum and numVal is not None and elem.get('extends') is None: - if minName is None: - minName = maxName = name - minValue = maxValue = numVal - elif minValue is None or numVal < minValue: - minName = name - minValue = numVal - elif maxValue is None or numVal > maxValue: - maxName = name - maxValue = numVal - - # Now append the non-numeric enumerant values - body.extend(aliasText) - - # Generate min/max value tokens - legacy use case. - if isEnum and expand: - body.extend((f' {expandPrefix}_BEGIN_RANGE{expandSuffix} = {minName},', - f' {expandPrefix}_END_RANGE{expandSuffix} = {maxName},', - f' {expandPrefix}_RANGE_SIZE{expandSuffix} = ({maxName} - {minName} + 1),')) - - # Generate a range-padding value to ensure the enum is 32 bits, but - # only in code generators, so it does not appear in documentation - if (self.genOpts.codeGenerator or - self.conventions.generate_max_enum_in_docs): - body.append(f' {expandPrefix}_MAX_ENUM{expandSuffix} = 0x7FFFFFFF') - - # Postfix - body.append("} %s;" % groupName) - - # Determine appropriate section for this declaration - if groupElem.get('type') == 'bitmask': - section = 'bitmask' - else: - section = 'group' - - return (section, '\n'.join(body)) - def buildConstantCDecl(self, enuminfo, name, alias): """Generate the C declaration for a constant (a single value). @@ -828,18 +407,7 @@ class OutputGenerator: (_, strVal) = self.enumToValue(enuminfo.elem, False) - if self.misracppstyle() and enuminfo.elem.get('type') and not alias: - # Generate e.g.: static constexpr uint32_t x = ~static_cast(1U); - # This appeases MISRA "underlying type" rules. - typeStr = enuminfo.elem.get('type'); - invert = '~' in strVal - number = strVal.strip("()~UL") - if typeStr != "float": - number += 'U' - strVal = "~" if invert else "" - strVal += "static_cast<" + typeStr + ">(" + number + ")" - body = 'static constexpr ' + typeStr.ljust(9) + name.ljust(33) + ' {' + strVal + '};' - elif enuminfo.elem.get('type') and not alias: + if enuminfo.elem.get('type') and not alias: # Generate e.g.: #define x (~0ULL) typeStr = enuminfo.elem.get('type'); invert = '~' in strVal @@ -860,18 +428,6 @@ class OutputGenerator: return body - def makeDir(self, path): - """Create a directory, if not already done. - - Generally called from derived generators creating hierarchies.""" - self.logMsg('diag', 'OutputGenerator::makeDir(' + path + ')') - if path not in self.madeDirs: - # This can get race conditions with multiple writers, see - # https://stackoverflow.com/questions/273192/ - if not os.path.exists(path): - os.makedirs(path) - self.madeDirs[path] = None - def beginFile(self, genOpts): """Start a new interface file @@ -880,24 +436,6 @@ class OutputGenerator: self.genOpts = genOpts if self.genOpts is None: raise MissingGeneratorOptionsError() - if self.genOpts.conventions is None: - raise MissingGeneratorOptionsConventionsError() - self.should_insert_may_alias_macro = \ - self.genOpts.conventions.should_insert_may_alias_macro(self.genOpts) - self.file_suffix = self.genOpts.conventions.file_suffix - - # Try to import the API dictionary, apimap.py, if it exists. Nothing - # in apimap.py cannot be extracted directly from the XML, and in the - # future we should do that. - if self.genOpts.genpath is not None: - try: - sys.path.insert(0, self.genOpts.genpath) - import apimap - self.apidict = apimap - except ImportError: - self.apidict = None - - self.conventions = genOpts.conventions # Open a temporary file for accumulating output. if self.genOpts.filename is not None: @@ -939,8 +477,6 @@ class OutputGenerator: self.emit = emit self.featureName = interface.get('name') self.featureType = interface.get('type') - # If there is an additional 'protect' attribute in the feature, save it - self.featureExtraProtect = interface.get('protect') def endFeature(self): """Finish an interface file, closing it when done. @@ -948,21 +484,6 @@ class OutputGenerator: Derived classes responsible for emitting feature""" self.featureName = None self.featureType = None - self.featureExtraProtect = None - - def genRequirements(self, name, mustBeFound = True): - """Generate text showing what core versions and extensions introduce - an API. This exists in the base Generator class because it is used by - the shared enumerant-generating interfaces (buildEnumCDecl, etc.). - Here it returns an empty string for most generators, but can be - overridden by e.g. DocGenerator. - - - name - name of the API - - mustBeFound - If True, when requirements for 'name' cannot be - determined, a warning comment is generated. - """ - - return '' def validateFeature(self, featureType, featureName): """Validate we are generating something only inside a `` tag""" @@ -1019,46 +540,6 @@ class OutputGenerator: Extend to generate as desired in your derived class.""" self.validateFeature('command', cmdinfo) - def genSpirv(self, spirv, spirvinfo, alias): - """Generate interface for a spirv element. - - - spirvinfo - SpirvInfo for a command - - Extend to generate as desired in your derived class.""" - return - - def genFormat(self, format, formatinfo, alias): - """Generate interface for a format element. - - - formatinfo - FormatInfo - - Extend to generate as desired in your derived class.""" - return - - def genSyncStage(self, stageinfo): - """Generate interface for a sync stage element. - - - stageinfo - SyncStageInfo - - Extend to generate as desired in your derived class.""" - return - - def genSyncAccess(self, accessinfo): - """Generate interface for a sync stage element. - - - accessinfo - AccessInfo - - Extend to generate as desired in your derived class.""" - return - - def genSyncPipeline(self, pipelineinfo): - """Generate interface for a sync stage element. - - - pipelineinfo - SyncPipelineInfo - - Extend to generate as desired in your derived class.""" - return - def makeProtoName(self, name, tail): """Turn a `` `` into C-language prototype and typedef declarations for that name. @@ -1085,8 +566,6 @@ class OutputGenerator: at this column""" if self.genOpts is None: raise MissingGeneratorOptionsError() - if self.genOpts.conventions is None: - raise MissingGeneratorOptionsConventionsError() indent = ' ' paramdecl = indent prefix = noneStr(param.text) @@ -1095,11 +574,7 @@ class OutputGenerator: text = noneStr(elem.text) tail = noneStr(elem.tail) - if self.should_insert_may_alias_macro and self.genOpts.conventions.is_voidpointer_alias(elem.tag, text, tail): - # OpenXR-specific macro insertion - but not in apiinc for the spec - tail = self.genOpts.conventions.make_voidpointer_alias(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) @@ -1108,14 +583,8 @@ class OutputGenerator: # text. paramdecl = paramdecl.ljust(aligncol - 1) + ' ' newLen = len(paramdecl) - self.logMsg('diag', 'Adjust length of parameter decl from', oldLen, 'to', newLen, ':', paramdecl) - if (self.misracppstyle() and prefix.find('const ') != -1): - # Change pointer type order from e.g. "const void *" to "void const *". - # If the string starts with 'const', reorder it to be after the first type. - paramdecl += prefix.replace('const ', '') + text + ' const' + tail - else: - paramdecl += prefix + text + tail + paramdecl += prefix + text + tail # Clear prefix for subsequent iterations prefix = '' @@ -1135,8 +604,6 @@ class OutputGenerator: - param - Element (`` or ``) to identify""" if self.genOpts is None: raise MissingGeneratorOptionsError() - if self.genOpts.conventions is None: - raise MissingGeneratorOptionsConventionsError() # Allow for missing tag newLen = 0 @@ -1145,52 +612,21 @@ class OutputGenerator: text = noneStr(elem.text) tail = noneStr(elem.tail) - if self.should_insert_may_alias_macro and self.genOpts.conventions.is_voidpointer_alias(elem.tag, text, tail): - # OpenXR-specific macro insertion - tail = self.genOpts.conventions.make_voidpointer_alias(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 def getMaxCParamTypeLength(self, info): """Return the length of the longest type field for a member/parameter. - - info - TypeInfo or CommandInfo. """ lengths = (self.getCParamTypeLength(member) for member in info.getMembers()) return max(lengths) - def getHandleParent(self, typename): - """Get the parent of a handle object.""" - if self.registry is None: - raise MissingRegistryError() - - info = self.registry.typedict.get(typename) - if info is None: - return None - - elem = info.elem - if elem is not None: - return elem.get('parent') - - return None - - def iterateHandleAncestors(self, typename): - """Iterate through the ancestors of a handle type.""" - current = self.getHandleParent(typename) - while current is not None: - yield current - current = self.getHandleParent(current) - - def getHandleAncestors(self, typename): - """Get the ancestors of a handle object.""" - return list(self.iterateHandleAncestors(typename)) - def getTypeCategory(self, typename): """Get the category of a type.""" if self.registry is None: @@ -1208,28 +644,22 @@ class OutputGenerator: def isStructAlwaysValid(self, structname): """Try to do check if a structure is always considered valid (i.e. there is no rules to its acceptance).""" # A conventions object is required for this call. - if not self.conventions: - raise RuntimeError("To use isStructAlwaysValid, be sure your options include a Conventions object.") if self.registry is None: raise MissingRegistryError() - if self.conventions.type_always_valid(structname): + if type_always_valid(structname): return True category = self.getTypeCategory(structname) - if self.conventions.category_requires_validation(category): + if category_requires_validation(category): return False info = self.registry.typedict.get(structname) - if info is None: - self.logMsg('error', f'isStructAlwaysValid({structname}) - structure not found in typedict') - members = info.getMembers() for member in members: member_name = getElemName(member) - if member_name in (self.conventions.structtype_member_name, - self.conventions.nextpointer_member_name): + if member_name in ('sType', 'pNext'): return False if member.get('noautovalidity'): @@ -1240,12 +670,12 @@ class OutputGenerator: if member_type in ('void', 'char') or self.paramIsArray(member) or self.paramIsPointer(member): return False - if self.conventions.type_always_valid(member_type): + if type_always_valid(member_type): continue member_category = self.getTypeCategory(member_type) - if self.conventions.category_requires_validation(member_category): + if category_requires_validation(member_category): return False if member_category in ('struct', 'union'): @@ -1275,8 +705,6 @@ class OutputGenerator: - elem - `` element to test""" required = elem.get('required') is not None - self.logMsg('diag', 'isEnumRequired:', elem.get('name'), - '->', required) return required # @@@ This code is overridden by equivalent code now run in @@ -1288,7 +716,7 @@ class OutputGenerator: if extname is not None: # 'supported' attribute was injected when the element was # moved into the group in Registry.parseTree() - if self.genOpts.defaultExtensions == elem.get('supported'): + if 'vulkan' == elem.get('supported'): required = True elif re.match(self.genOpts.addExtensions, extname) is not None: required = True @@ -1309,7 +737,7 @@ class OutputGenerator: proto = cmd.find('proto') params = cmd.findall('param') # Begin accumulating prototype and typedef strings - pdecl = self.genOpts.apicall + pdecl = 'VKAPI_ATTR ' tdecl = 'typedef ' # Insert the function return type/name. @@ -1359,27 +787,8 @@ class OutputGenerator: paramdecl = '(' if n > 0: paramnames = [] - if self.misracppstyle(): - for p in params: - param = '' - firstIter = True; - for t in p.itertext(): - if (firstIter): - prefix = t - firstIter = False - else: - # Change pointer type order from e.g. "const void *" to "void const *". - # If the string starts with 'const', reorder it to be after the first type. - if (prefix.find('const ') != -1): - param += prefix.replace('const ', '') + t + ' const ' - else: - param += prefix + t - # Clear prefix for subsequent iterations - prefix = '' - paramnames.append(param); - else: - paramnames = (''.join(t for t in p.itertext()) - for p in params) + paramnames = (''.join(t for t in p.itertext()) + for p in params) paramdecl += ', '.join(paramnames) else: paramdecl += 'void' diff --git a/src/gfxstream/codegen/scripts/genvk.py b/src/gfxstream/codegen/scripts/genvk.py index eb231f6ff02..b8b24c9aeb6 100755 --- a/src/gfxstream/codegen/scripts/genvk.py +++ b/src/gfxstream/codegen/scripts/genvk.py @@ -1,16 +1,14 @@ #!/usr/bin/python3 # # Copyright 2013-2023 The Khronos Group Inc. +# Copyright 2023-2024 Google Inc. # # SPDX-License-Identifier: Apache-2.0 import argparse import os -import pdb import re import sys -import copy -import time import xml.etree.ElementTree as etree sys.path.append(os.path.abspath(os.path.dirname(__file__))) @@ -19,28 +17,12 @@ from cgenerator import CGeneratorOptions, COutputGenerator from generator import write from reg import Registry -from apiconventions import APIConventions # gfxstream + cereal modules from cerealgenerator import CerealGenerator -# Simple timer functions -startTime = None from typing import Optional -def startTimer(timeit): - global startTime - if timeit: - startTime = time.process_time() - - -def endTimer(timeit, msg): - global startTime - if timeit and startTime is not None: - endTime = time.process_time() - startTime = None - - def makeREstring(strings, default=None, strings_are_regex=False): """Turn a list of strings into a regexp string matching exactly those strings.""" if strings or default is None: @@ -59,53 +41,17 @@ def makeGenOpts(args): global genOpts genOpts = {} - # Default class of extensions to include, or None - defaultExtensions = args.defaultExtensions - - # Additional extensions to include (list of extensions) - extensions = args.extension - - # Extensions to remove (list of extensions) - removeExtensions = args.removeExtensions - - # Extensions to emit (list of extensions) - emitExtensions = args.emitExtensions - - # SPIR-V capabilities / features to emit (list of extensions & capabilities) - emitSpirv = args.emitSpirv - - # Vulkan Formats to emit - emitFormats = args.emitFormats - - # Features to include (list of features) - features = args.feature - - # Whether to disable inclusion protect in headers - protect = args.protect - # Output target directory directory = args.directory - # Path to generated files, particularly apimap.py - genpath = args.genpath - - # Generate MISRA C-friendly headers - misracstyle = args.misracstyle; - - # Generate MISRA C++-friendly headers - misracppstyle = args.misracppstyle; - # Descriptive names for various regexp patterns used to select # versions and extensions - allFormats = allSpirv = allFeatures = allExtensions = r'.*' + allFormats = allFeatures = allExtensions = r'.*' # Turn lists of names/patterns into matching regular expressions - addExtensionsPat = makeREstring(extensions, None) - removeExtensionsPat = makeREstring(removeExtensions, None) - emitExtensionsPat = makeREstring(emitExtensions, allExtensions) - emitSpirvPat = makeREstring(emitSpirv, allSpirv) - emitFormatsPat = makeREstring(emitFormats, allFormats) - featuresPat = makeREstring(features, allFeatures) + emitExtensionsPat = makeREstring([], allExtensions) + emitFormatsPat = makeREstring([], allFormats) + featuresPat = makeREstring([], allFeatures) # Copyright text prefixing all headers (list of strings). # The SPDX formatting below works around constraints of the 'reuse' tool @@ -127,200 +73,15 @@ def makeGenOpts(args): '' ] - vulkanLayer = args.vulkanLayer - - # Defaults for generating re-inclusion protection wrappers (or not) - protectFile = protect - - # An API style conventions object - conventions = APIConventions() - - if args.apiname is not None: - defaultAPIName = args.apiname - else: - defaultAPIName = conventions.xml_api_name - - # APIs to merge - mergeApiNames = args.mergeApiNames - - isCTS = args.isCTS - - # Platform extensions, in their own header files - # Each element of the platforms[] array defines information for - # generating a single platform: - # [0] is the generated header file name - # [1] is the set of platform extensions to generate - # [2] is additional extensions whose interfaces should be considered, - # but suppressed in the output, to avoid duplicate definitions of - # dependent types like VkDisplayKHR and VkSurfaceKHR which come from - # non-platform extensions. - - # Track all platform extensions, for exclusion from vulkan_core.h - allPlatformExtensions = [] - - # Extensions suppressed for all WSI platforms (WSI extensions required - # by all platforms) - commonSuppressExtensions = [ 'VK_KHR_display', 'VK_KHR_swapchain' ] - - # Extensions required and suppressed for beta "platform". This can - # probably eventually be derived from the requires= attributes of - # the extension blocks. - betaRequireExtensions = [ - 'VK_KHR_portability_subset', - 'VK_KHR_video_encode_queue', - 'VK_EXT_video_encode_h264', - 'VK_EXT_video_encode_h265', - 'VK_NV_displacement_micromap', - 'VK_AMDX_shader_enqueue', - ] - - betaSuppressExtensions = [ - 'VK_KHR_video_queue', - 'VK_EXT_opacity_micromap', - 'VK_KHR_pipeline_library', - ] - - platforms = [ - [ 'vulkan_android.h', [ 'VK_KHR_android_surface', - 'VK_ANDROID_external_memory_android_hardware_buffer', - 'VK_ANDROID_external_format_resolve' - ], commonSuppressExtensions + - [ 'VK_KHR_format_feature_flags2', - ] ], - [ 'vulkan_fuchsia.h', [ 'VK_FUCHSIA_imagepipe_surface', - 'VK_FUCHSIA_external_memory', - 'VK_FUCHSIA_external_semaphore', - 'VK_FUCHSIA_buffer_collection' ], commonSuppressExtensions ], - [ 'vulkan_ggp.h', [ 'VK_GGP_stream_descriptor_surface', - 'VK_GGP_frame_token' ], commonSuppressExtensions ], - [ 'vulkan_ios.h', [ 'VK_MVK_ios_surface' ], commonSuppressExtensions ], - [ 'vulkan_macos.h', [ 'VK_MVK_macos_surface' ], commonSuppressExtensions ], - [ 'vulkan_vi.h', [ 'VK_NN_vi_surface' ], commonSuppressExtensions ], - [ 'vulkan_wayland.h', [ 'VK_KHR_wayland_surface' ], commonSuppressExtensions ], - [ 'vulkan_win32.h', [ 'VK_.*_win32(|_.*)', 'VK_.*_winrt(|_.*)', 'VK_EXT_full_screen_exclusive' ], - commonSuppressExtensions + - [ 'VK_KHR_external_semaphore', - 'VK_KHR_external_memory_capabilities', - 'VK_KHR_external_fence', - 'VK_KHR_external_fence_capabilities', - 'VK_KHR_get_surface_capabilities2', - 'VK_NV_external_memory_capabilities', - ] ], - [ 'vulkan_xcb.h', [ 'VK_KHR_xcb_surface' ], commonSuppressExtensions ], - [ 'vulkan_xlib.h', [ 'VK_KHR_xlib_surface' ], commonSuppressExtensions ], - [ 'vulkan_directfb.h', [ 'VK_EXT_directfb_surface' ], commonSuppressExtensions ], - [ 'vulkan_xlib_xrandr.h', [ 'VK_EXT_acquire_xlib_display' ], commonSuppressExtensions ], - [ 'vulkan_metal.h', [ 'VK_EXT_metal_surface', - 'VK_EXT_metal_objects' ], commonSuppressExtensions ], - [ 'vulkan_screen.h', [ 'VK_QNX_screen_surface', - 'VK_QNX_external_memory_screen_buffer' ], commonSuppressExtensions ], - [ 'vulkan_sci.h', [ 'VK_NV_external_sci_sync', - 'VK_NV_external_sci_sync2', - 'VK_NV_external_memory_sci_buf'], commonSuppressExtensions ], - [ 'vulkan_beta.h', betaRequireExtensions, betaSuppressExtensions ], - ] - - for platform in platforms: - headername = platform[0] - - allPlatformExtensions += platform[1] - - addPlatformExtensionsRE = makeREstring( - platform[1] + platform[2], strings_are_regex=True) - emitPlatformExtensionsRE = makeREstring( - platform[1], strings_are_regex=True) - - opts = CGeneratorOptions( - conventions = conventions, - filename = headername, - directory = directory, - genpath = None, - apiname = defaultAPIName, - mergeApiNames = mergeApiNames, - profile = None, - versions = featuresPat, - emitversions = None, - defaultExtensions = None, - addExtensions = addPlatformExtensionsRE, - removeExtensions = None, - emitExtensions = emitPlatformExtensionsRE, - prefixText = prefixStrings + vkPrefixStrings, - genFuncPointers = True, - protectFile = protectFile, - protectFeature = False, - protectProto = '#ifndef', - protectProtoStr = 'VK_NO_PROTOTYPES', - apicall = 'VKAPI_ATTR ', - apientry = 'VKAPI_CALL ', - apientryp = 'VKAPI_PTR *', - alignFuncParam = 48, - misracstyle = misracstyle, - misracppstyle = misracppstyle) - - genOpts[headername] = [ COutputGenerator, opts ] - - # Header for core API + extensions. - # To generate just the core API, - # change to 'defaultExtensions = None' below. - # - # By default this adds all enabled, non-platform extensions. - # It removes all platform extensions (from the platform headers options - # constructed above) as well as any explicitly specified removals. - - removeExtensionsPat = makeREstring( - allPlatformExtensions + removeExtensions, None, strings_are_regex=True) - - genOpts['vulkan_core.h'] = [ - COutputGenerator, - CGeneratorOptions( - conventions = conventions, - filename = 'vulkan_core.h', - directory = directory, - genpath = None, - apiname = defaultAPIName, - mergeApiNames = mergeApiNames, - profile = None, - versions = featuresPat, - emitversions = featuresPat, - defaultExtensions = defaultExtensions, - addExtensions = addExtensionsPat, - removeExtensions = removeExtensionsPat, - emitExtensions = emitExtensionsPat, - prefixText = prefixStrings + vkPrefixStrings, - genFuncPointers = True, - protectFile = protectFile, - protectFeature = False, - protectProto = '#ifndef', - protectProtoStr = 'VK_NO_PROTOTYPES', - apicall = 'VKAPI_ATTR ', - apientry = 'VKAPI_CALL ', - apientryp = 'VKAPI_PTR *', - alignFuncParam = 48, - misracstyle = misracstyle, - misracppstyle = misracppstyle) - ] - - # Serializer for spec genOpts['cereal'] = [ CerealGenerator, CGeneratorOptions( - conventions = conventions, directory = directory, - apiname = 'vulkan', - profile = None, versions = featuresPat, emitversions = featuresPat, - defaultExtensions = defaultExtensions, addExtensions = None, - removeExtensions = None, emitExtensions = emitExtensionsPat, prefixText = prefixStrings + vkPrefixStrings, - genFuncPointers = True, - protectFile = protectFile, - protectFeature = False, - protectProto = '#ifndef', - protectProtoStr = 'VK_NO_PROTOTYPES', - apicall = 'VKAPI_ATTR ', apientry = 'VKAPI_CALL ', apientryp = 'VKAPI_PTR *', alignFuncParam = 48) @@ -337,32 +98,18 @@ def makeGenOpts(args): genOpts['vulkan_gfxstream.h'] = [ COutputGenerator, CGeneratorOptions( - conventions = conventions, filename = 'vulkan_gfxstream.h', directory = directory, - genpath = None, - apiname = 'vulkan', - profile = None, versions = featuresPat, emitversions = None, - defaultExtensions = None, addExtensions = makeREstring(['VK_GOOGLE_gfxstream'], None), - removeExtensions = None, emitExtensions = makeREstring(['VK_GOOGLE_gfxstream'], None), prefixText = prefixStrings + vkPrefixStrings + gfxstreamPrefixStrings, - genFuncPointers = True, # Use #pragma once in the prefixText instead, so that we can put the copyright comments # at the beginning of the file. - protectFile = False, - protectFeature = False, - protectProto = '#ifndef', - protectProtoStr = 'VK_NO_PROTOTYPES', - apicall = 'VKAPI_ATTR ', apientry = 'VKAPI_CALL ', apientryp = 'VKAPI_PTR *', - alignFuncParam = 48, - misracstyle = misracstyle, - misracppstyle = misracppstyle) + alignFuncParam = 48) ] def genTarget(args): @@ -375,7 +122,6 @@ def genTarget(args): - target - target to generate - directory - directory to generate it in - - protect - True if re-inclusion wrappers should be created - extensions - list of additional extensions to include in generated interfaces""" # Create generator options with parameters specified on command line @@ -400,92 +146,22 @@ def genTarget(args): # of names, or a regular expression. if __name__ == '__main__': parser = argparse.ArgumentParser() - - parser.add_argument('-apiname', action='store', - default=None, - help='Specify API to generate (defaults to repository-specific conventions object value)') - parser.add_argument('-mergeApiNames', action='store', - default=None, - help='Specify a comma separated list of APIs to merge into the target API') - parser.add_argument('-defaultExtensions', action='store', - default=APIConventions().xml_api_name, - help='Specify a single class of extensions to add to targets') - parser.add_argument('-extension', action='append', - default=[], - help='Specify an extension or extensions to add to targets') - parser.add_argument('-removeExtensions', action='append', - default=[], - help='Specify an extension or extensions to remove from targets') - parser.add_argument('-emitExtensions', action='append', - default=[], - help='Specify an extension or extensions to emit in targets') - parser.add_argument('-emitSpirv', action='append', - default=[], - help='Specify a SPIR-V extension or capability to emit in targets') - parser.add_argument('-emitFormats', action='append', - default=[], - help='Specify Vulkan Formats to emit in targets') - parser.add_argument('-feature', action='append', - default=[], - help='Specify a core API feature name or names to add to targets') - parser.add_argument('-debug', action='store_true', - help='Enable debugging') - parser.add_argument('-dump', action='store_true', - help='Enable dump to stderr') - parser.add_argument('-diagfile', action='store', - default=None, - help='Write diagnostics to specified file') - parser.add_argument('-errfile', action='store', - default=None, - help='Write errors and warnings to specified file instead of stderr') - parser.add_argument('-noprotect', dest='protect', action='store_false', - help='Disable inclusion protection in output headers') - parser.add_argument('-profile', action='store_true', - help='Enable profiling') parser.add_argument('-registry', action='store', default='vk.xml', help='Use specified registry file instead of vk.xml') parser.add_argument('-registryGfxstream', action='store', default=None, help='Use specified gfxstream registry file') - parser.add_argument('-time', action='store_true', - help='Enable timing') - parser.add_argument('-genpath', action='store', default='gen', - help='Path to generated files') parser.add_argument('-o', action='store', dest='directory', default='.', help='Create target and related files in specified directory') parser.add_argument('target', metavar='target', nargs='?', help='Specify target') - parser.add_argument('-quiet', action='store_true', default=True, - help='Suppress script output during normal execution.') - parser.add_argument('-verbose', action='store_false', dest='quiet', default=True, - help='Enable script output during normal execution.') - parser.add_argument('--vulkanLayer', action='store_true', dest='vulkanLayer', - help='Enable scripts to generate VK specific vulkan_json_data.hpp for json_gen_layer.') - parser.add_argument('-misracstyle', dest='misracstyle', action='store_true', - help='generate MISRA C-friendly headers') - parser.add_argument('-misracppstyle', dest='misracppstyle', action='store_true', - help='generate MISRA C++-friendly headers') - parser.add_argument('--iscts', action='store_true', dest='isCTS', - help='Specify if this should generate CTS compatible code') args = parser.parse_args() - # This splits arguments which are space-separated lists - args.feature = [name for arg in args.feature for name in arg.split()] - args.extension = [name for arg in args.extension for name in arg.split()] - - # create error/warning & diagnostic files - if args.errfile: - errWarn = open(args.errfile, 'w', encoding='utf-8') - else: - errWarn = sys.stderr - - if args.diagfile: - diag = open(args.diagfile, 'w', encoding='utf-8') - else: - diag = None + errWarn = sys.stderr + diag = None # Create the API generator & generator options (gen, options) = genTarget(args) @@ -495,9 +171,7 @@ if __name__ == '__main__': reg = Registry(gen, options) # Parse the specified registry XML into an ElementTree object - startTimer(args.time) tree = etree.parse(args.registry) - endTimer(args.time, '* Time to make ElementTree =') # Merge the gfxstream registry with the official Vulkan registry if the # target is the cereal generator @@ -530,7 +204,6 @@ if __name__ == '__main__': if name not in originalEntryDict.keys(): treeEntries.append(entry) continue - print(f'Entry {entriesName}:{name}') originalEntry = originalEntryDict[name] @@ -553,17 +226,5 @@ if __name__ == '__main__': originalEntry.append(child) # Load the XML tree into the registry object - startTimer(args.time) reg.loadElementTree(tree) - endTimer(args.time, '* Time to parse ElementTree =') - - if args.dump: - reg.dumpReg(filehandle=open('regdump.txt', 'w', encoding='utf-8')) - - # Finally, use the output generator to create the requested target - if args.debug: - pdb.run('reg.apiGen()') - else: - startTimer(args.time) - reg.apiGen() - endTimer(args.time, '* Time to generate ' + args.target + ' =') + reg.apiGen() diff --git a/src/gfxstream/codegen/scripts/reg.py b/src/gfxstream/codegen/scripts/reg.py index 4b5a80f7139..803825b5e5d 100644 --- a/src/gfxstream/codegen/scripts/reg.py +++ b/src/gfxstream/codegen/scripts/reg.py @@ -1,6 +1,7 @@ #!/usr/bin/python3 -i # # Copyright 2013-2023 The Khronos Group Inc. +# Copyright 2023-2024 Google Inc. # # SPDX-License-Identifier: Apache-2.0 @@ -13,7 +14,6 @@ import xml.etree.ElementTree as etree from collections import defaultdict, deque, namedtuple from generator import GeneratorOptions, OutputGenerator, noneStr, write -from apiconventions import APIConventions def apiNameMatch(str, supported): """Return whether a required api name matches a pattern specified for an @@ -30,43 +30,8 @@ def apiNameMatch(str, supported): # Fallthrough case - either str is None or the test failed return False -def matchAPIProfile(api, profile, elem): +def matchAPIProfile(api, elem): """Return whether an API and profile - being generated matches an element's profile - - - api - string naming the API to match - - profile - string naming the profile to match - - elem - Element which (may) have 'api' and 'profile' - attributes to match to. - - If a tag is not present in the Element, the corresponding API - or profile always matches. - - Otherwise, the tag must exactly match the API or profile. - - Thus, if 'profile' = core: - - - `` with no attribute will match - - `` will match - - `` will not match - - Possible match conditions: - - ``` - Requested Element - Profile Profile - --------- -------- - None None Always matches - 'string' None Always matches - None 'string' Does not match. Cannot generate multiple APIs - or profiles, so if an API/profile constraint - is present, it must be asked for explicitly. - 'string' 'string' Strings must match - ``` - - ** In the future, we will allow regexes for the attributes, - not just strings, so that `api="^(gl|gles2)"` will match. Even - this is not really quite enough, we might prefer something like `"gl(core)|gles1(common-lite)"`.""" # Match 'api', if present elem_api = elem.get('api') @@ -77,120 +42,8 @@ def matchAPIProfile(api, profile, elem): elif api != elem_api: # Requested API does not match attribute return False - elem_profile = elem.get('profile') - if elem_profile: - if profile is None: - raise UserWarning("No profile requested, but 'profile' attribute is present with value '" - + elem_profile + "'") - elif profile != elem_profile: - # Requested profile does not match attribute - return False return True - -def mergeAPIs(tree, fromApiNames, toApiName): - """Merge multiple APIs using the precedence order specified in apiNames. - Also deletes elements. - - tree - Element at the root of the hierarchy to merge. - apiNames - list of strings of API names.""" - - stack = deque() - stack.append(tree) - - while len(stack) > 0: - parent = stack.pop() - - for child in parent.findall('*'): - if child.tag == 'remove': - # Remove elements - parent.remove(child) - else: - stack.append(child) - - supportedList = child.get('supported') - if supportedList: - supportedList = supportedList.split(',') - for apiName in [toApiName] + fromApiNames: - if apiName in supportedList: - child.set('supported', toApiName) - - if child.get('api'): - definitionName = None - definitionVariants = [] - - # Keep only one definition with the same name if there are multiple definitions - if child.tag in ['type']: - if child.get('name') is not None: - definitionName = child.get('name') - definitionVariants = parent.findall(f"{child.tag}[@name='{definitionName}']") - else: - definitionName = child.find('name').text - definitionVariants = parent.findall(f"{child.tag}/name[.='{definitionName}']/..") - elif child.tag in ['member', 'param']: - definitionName = child.find('name').text - definitionVariants = parent.findall(f"{child.tag}/name[.='{definitionName}']/..") - elif child.tag in ['enum', 'feature']: - definitionName = child.get('name') - definitionVariants = parent.findall(f"{child.tag}[@name='{definitionName}']") - elif child.tag in ['require']: - definitionName = child.get('feature') - definitionVariants = parent.findall(f"{child.tag}[@feature='{definitionName}']") - elif child.tag in ['command']: - definitionName = child.find('proto/name').text - definitionVariants = parent.findall(f"{child.tag}/proto/name[.='{definitionName}']/../..") - - if definitionName: - bestMatchApi = None - requires = None - for apiName in [toApiName] + fromApiNames: - for variant in definitionVariants: - # Keep any requires attributes from the target API - if variant.get('requires') and variant.get('api') == apiName: - requires = variant.get('requires') - # Find the best matching definition - if apiName in variant.get('api').split(',') and bestMatchApi is None: - bestMatchApi = variant.get('api') - - if bestMatchApi: - for variant in definitionVariants: - if variant.get('api') != bestMatchApi: - # Only keep best matching definition - parent.remove(variant) - else: - # Add requires attribute from the target API if it is not overridden - if requires is not None and variant.get('requires') is None: - variant.set('requires', requires) - variant.set('api', toApiName) - - -def stripNonmatchingAPIs(tree, apiName, actuallyDelete = True): - """Remove tree Elements with 'api' attributes matching apiName. - - tree - Element at the root of the hierarchy to strip. Only its - children can actually be removed, not the tree itself. - apiName - string which much match a command-separated component of - the 'api' attribute. - actuallyDelete - only delete matching elements if True.""" - - stack = deque() - stack.append(tree) - - while len(stack) > 0: - parent = stack.pop() - - for child in parent.findall('*'): - api = child.get('api') - - if apiNameMatch(apiName, api): - # Add child to the queue - stack.append(child) - elif not apiNameMatch(apiName, api): - # Child does not match requested api. Remove it. - if actuallyDelete: - parent.remove(child) - - class BaseInfo: """Base class for information about a registry feature (type/group/enum/command/API/extension). @@ -361,43 +214,6 @@ class FeatureInfo(BaseInfo): self.supported = elem.get('supported', 'disabled') -class SpirvInfo(BaseInfo): - """Registry information about an API - or .""" - - def __init__(self, elem): - BaseInfo.__init__(self, elem) - -class FormatInfo(BaseInfo): - """Registry information about an API .""" - - def __init__(self, elem, condition): - BaseInfo.__init__(self, elem) - # Need to save the condition here when it is known - self.condition = condition - -class SyncStageInfo(BaseInfo): - """Registry information about .""" - - def __init__(self, elem, condition): - BaseInfo.__init__(self, elem) - # Need to save the condition here when it is known - self.condition = condition - -class SyncAccessInfo(BaseInfo): - """Registry information about .""" - - def __init__(self, elem, condition): - BaseInfo.__init__(self, elem) - # Need to save the condition here when it is known - self.condition = condition - -class SyncPipelineInfo(BaseInfo): - """Registry information about .""" - - def __init__(self, elem): - BaseInfo.__init__(self, elem) - class Registry: """Object representing an API registry, loaded from an XML file.""" @@ -412,7 +228,7 @@ class Registry: if genOpts is None: # If no generator is provided, we may still need the XML API name # (for example, in genRef.py). - self.genOpts = GeneratorOptions(apiname = APIConventions().xml_api_name) + self.genOpts = GeneratorOptions(apiname = 'vulkan') else: self.genOpts = genOpts "Options controlling features to write and how to format them" @@ -445,44 +261,10 @@ class Registry: self.extdict = {} "dictionary of FeatureInfo objects for `` elements keyed by extension name" - self.spirvextdict = {} - "dictionary of FeatureInfo objects for `` elements keyed by spirv extension name" - - self.spirvcapdict = {} - "dictionary of FeatureInfo objects for `` elements keyed by spirv capability name" - - self.formatsdict = {} - "dictionary of FeatureInfo objects for `` elements keyed by VkFormat name" - - self.syncstagedict = {} - "dictionary of Sync*Info objects for `` elements keyed by VkPipelineStageFlagBits2 name" - - self.syncaccessdict = {} - "dictionary of Sync*Info objects for `` elements keyed by VkAccessFlagBits2 name" - - self.syncpipelinedict = {} - "dictionary of Sync*Info objects for `` elements keyed by pipeline type name" - self.emitFeatures = False """True to actually emit features for a version / extension, or False to just treat them as emitted""" - self.breakPat = None - "regexp pattern to break on when generating names" - # self.breakPat = re.compile('VkFenceImportFlagBits.*') - - self.requiredextensions = [] # Hack - can remove it after validity generator goes away - - # ** Global types for automatic source generation ** - # Length Member data - self.commandextensiontuple = namedtuple('commandextensiontuple', - ['command', # The name of the command being modified - 'value', # The value to append to the command - 'extension']) # The name of the extension that added it - self.validextensionstructs = defaultdict(list) - self.commandextensionsuccesses = [] - self.commandextensionerrors = [] - self.filename = None def loadElementTree(self, tree): @@ -515,13 +297,10 @@ class Registry: The dictionary key is the element 'name' attribute.""" - # self.gen.logMsg('diag', 'Adding ElementInfo.required =', - # info.required, 'name =', elem.get('name')) key = elem.get('name') if key in dictionary: if not dictionary[key].compareElem(info, infoName): - self.gen.logMsg('warn', 'Attempt to redefine', key, - '(this should not happen)') + return else: dictionary[key] = info @@ -534,20 +313,14 @@ class Registry: - fname - name of type / enum / command - dictionary - self.{type|enum|cmd}dict""" - key = (fname, self.genOpts.apiname) + key = (fname, 'vulkan') if key in dictionary: - # self.gen.logMsg('diag', 'Found API-specific element for feature', fname) return dictionary[key] if fname in dictionary: - # self.gen.logMsg('diag', 'Found generic element for feature', fname) return dictionary[fname] return None - def breakOnName(self, regexp): - """Specify a feature name regexp to break on when generating features.""" - self.breakPat = re.compile(regexp) - def parseTree(self): """Parse the registry Element, once created""" # This must be the Element for the root @@ -555,26 +328,6 @@ class Registry: raise RuntimeError("Tree not initialized!") self.reg = self.tree.getroot() - # Preprocess the tree in one of the following ways: - # - either merge a set of APIs to another API based on their 'api' attributes - # - or remove all elements with non-matching 'api' attributes - # The preprocessing happens through a breath-first tree traversal. - # This is a blunt hammer, but eliminates the need to track and test - # the apis deeper in processing to select the correct elements and - # avoid duplicates. - # Schema validation should prevent duplicate elements with - # overlapping api attributes, or where one element has an api - # attribute and the other does not. - - if self.genOpts.mergeApiNames: - mergeAPIs(self.reg, self.genOpts.mergeApiNames.split(','), self.genOpts.apiname) - else: - stripNonmatchingAPIs(self.reg, self.genOpts.apiname, actuallyDelete = True) - - # Create dictionary of registry types from toplevel tags - # and add 'name' attribute to each tag (where missing) - # based on its element. - # # There is usually one block; more are OK # Required attributes: 'name' or nested tag contents self.typedict = {} @@ -654,16 +407,9 @@ class Registry: # Replace the dictionary entry for the CmdInfo element self.cmddict[name] = ci - # @ newString = etree.tostring(base, encoding="unicode").replace(aliasValue, aliasName) - # @elem.append(etree.fromstring(replacement)) - else: - self.gen.logMsg('warn', 'No matching found for command', - cmd.get('name'), 'alias', alias) - # Create dictionaries of API and extension interfaces # from toplevel and tags. self.apidict = {} - format_condition = dict() for feature in self.reg.findall('feature'): featureInfo = FeatureInfo(feature) self.addElementInfo(feature, featureInfo, 'feature', self.apidict) @@ -687,36 +433,19 @@ class Registry: addEnumInfo = False groupName = enum.get('extends') if groupName is not None: - # self.gen.logMsg('diag', 'Found extension enum', - # enum.get('name')) # Add version number attribute to the element enum.set('version', featureInfo.version) # Look up the GroupInfo with matching groupName if groupName in self.groupdict: - # self.gen.logMsg('diag', 'Matching group', - # groupName, 'found, adding element...') gi = self.groupdict[groupName] gi.elem.append(copy.deepcopy(enum)) - else: - self.gen.logMsg('warn', 'NO matching group', - groupName, 'for enum', enum.get('name'), 'found.') - if groupName == "VkFormat": - format_name = enum.get('name') - if enum.get('alias'): - format_name = enum.get('alias') - format_condition[format_name] = featureInfo.name addEnumInfo = True elif enum.get('value') or enum.get('bitpos') or enum.get('alias'): - # self.gen.logMsg('diag', 'Adding extension constant "enum"', - # enum.get('name')) addEnumInfo = True if addEnumInfo: enumInfo = EnumInfo(enum) self.addElementInfo(enum, enumInfo, 'enum', self.enumdict) - sync_pipeline_stage_condition = dict() - sync_access_condition = dict() - self.extensions = self.reg.findall('extensions/extension') self.extdict = {} for feature in self.extensions: @@ -734,8 +463,6 @@ class Registry: addEnumInfo = False groupName = enum.get('extends') if groupName is not None: - # self.gen.logMsg('diag', 'Found extension enum', - # enum.get('name')) # Add block's extension number attribute to # the element unless specified explicitly, such @@ -748,139 +475,22 @@ class Registry: enum.set('supported', noneStr(featureInfo.supported)) # Look up the GroupInfo with matching groupName if groupName in self.groupdict: - # self.gen.logMsg('diag', 'Matching group', - # groupName, 'found, adding element...') gi = self.groupdict[groupName] gi.elem.append(copy.deepcopy(enum)) - else: - self.gen.logMsg('warn', 'NO matching group', - groupName, 'for enum', enum.get('name'), 'found.') - # This is Vulkan-specific - if groupName == "VkFormat": - format_name = enum.get('name') - if enum.get('alias'): - format_name = enum.get('alias') - if format_name in format_condition: - format_condition[format_name] += "," + featureInfo.name - else: - format_condition[format_name] = featureInfo.name - elif groupName == "VkPipelineStageFlagBits2": - stage_flag = enum.get('name') - if enum.get('alias'): - stage_flag = enum.get('alias') - featureName = elem.get('depends') if elem.get('depends') is not None else featureInfo.name - if stage_flag in sync_pipeline_stage_condition: - sync_pipeline_stage_condition[stage_flag] += "," + featureName - else: - sync_pipeline_stage_condition[stage_flag] = featureName - elif groupName == "VkAccessFlagBits2": - access_flag = enum.get('name') - if enum.get('alias'): - access_flag = enum.get('alias') - featureName = elem.get('depends') if elem.get('depends') is not None else featureInfo.name - if access_flag in sync_access_condition: - sync_access_condition[access_flag] += "," + featureName - else: - sync_access_condition[access_flag] = featureName addEnumInfo = True elif enum.get('value') or enum.get('bitpos') or enum.get('alias'): - # self.gen.logMsg('diag', 'Adding extension constant "enum"', - # enum.get('name')) addEnumInfo = True if addEnumInfo: enumInfo = EnumInfo(enum) self.addElementInfo(enum, enumInfo, 'enum', self.enumdict) - # Parse out all spirv tags in dictionaries - # Use addElementInfo to catch duplicates - for spirv in self.reg.findall('spirvextensions/spirvextension'): - spirvInfo = SpirvInfo(spirv) - self.addElementInfo(spirv, spirvInfo, 'spirvextension', self.spirvextdict) - for spirv in self.reg.findall('spirvcapabilities/spirvcapability'): - spirvInfo = SpirvInfo(spirv) - self.addElementInfo(spirv, spirvInfo, 'spirvcapability', self.spirvcapdict) - - for format in self.reg.findall('formats/format'): - condition = None - format_name = format.get('name') - if format_name in format_condition: - condition = format_condition[format_name] - formatInfo = FormatInfo(format, condition) - self.addElementInfo(format, formatInfo, 'format', self.formatsdict) - - for stage in self.reg.findall('sync/syncstage'): - condition = None - stage_flag = stage.get('name') - if stage_flag in sync_pipeline_stage_condition: - condition = sync_pipeline_stage_condition[stage_flag] - syncInfo = SyncStageInfo(stage, condition) - self.addElementInfo(stage, syncInfo, 'syncstage', self.syncstagedict) - - for access in self.reg.findall('sync/syncaccess'): - condition = None - access_flag = access.get('name') - if access_flag in sync_access_condition: - condition = sync_access_condition[access_flag] - syncInfo = SyncAccessInfo(access, condition) - self.addElementInfo(access, syncInfo, 'syncaccess', self.syncaccessdict) - - for pipeline in self.reg.findall('sync/syncpipeline'): - syncInfo = SyncPipelineInfo(pipeline) - self.addElementInfo(pipeline, syncInfo, 'syncpipeline', self.syncpipelinedict) - - def dumpReg(self, maxlen=120, filehandle=sys.stdout): - """Dump all the dictionaries constructed from the Registry object. - - Diagnostic to dump the dictionaries to specified file handle (default stdout). - Truncates type / enum / command elements to maxlen characters (default 120)""" - write('***************************************', file=filehandle) - write(' ** Dumping Registry contents **', file=filehandle) - write('***************************************', file=filehandle) - write('// Types', file=filehandle) - for name in self.typedict: - tobj = self.typedict[name] - write(' Type', name, '->', etree.tostring(tobj.elem)[0:maxlen], file=filehandle) - write('// Groups', file=filehandle) - for name in self.groupdict: - gobj = self.groupdict[name] - write(' Group', name, '->', etree.tostring(gobj.elem)[0:maxlen], file=filehandle) - write('// Enums', file=filehandle) - for name in self.enumdict: - eobj = self.enumdict[name] - write(' Enum', name, '->', etree.tostring(eobj.elem)[0:maxlen], file=filehandle) - write('// Commands', file=filehandle) - for name in self.cmddict: - cobj = self.cmddict[name] - write(' Command', name, '->', etree.tostring(cobj.elem)[0:maxlen], file=filehandle) - write('// APIs', file=filehandle) - for key in self.apidict: - write(' API Version ', key, '->', - etree.tostring(self.apidict[key].elem)[0:maxlen], file=filehandle) - write('// Extensions', file=filehandle) - for key in self.extdict: - write(' Extension', key, '->', - etree.tostring(self.extdict[key].elem)[0:maxlen], file=filehandle) - write('// SPIR-V', file=filehandle) - for key in self.spirvextdict: - write(' SPIR-V Extension', key, '->', - etree.tostring(self.spirvextdict[key].elem)[0:maxlen], file=filehandle) - for key in self.spirvcapdict: - write(' SPIR-V Capability', key, '->', - etree.tostring(self.spirvcapdict[key].elem)[0:maxlen], file=filehandle) - write('// VkFormat', file=filehandle) - for key in self.formatsdict: - write(' VkFormat', key, '->', - etree.tostring(self.formatsdict[key].elem)[0:maxlen], file=filehandle) - def markTypeRequired(self, typename, required): """Require (along with its dependencies) or remove (but not its dependencies) a type. - typename - name of type - required - boolean (to tag features as required or not) """ - self.gen.logMsg('diag', 'tagging type:', typename, '-> required =', required) - # Get TypeInfo object for tag corresponding to typename typeinfo = self.lookupElementInfo(typename, self.typedict) if typeinfo is not None: @@ -891,43 +501,31 @@ class Registry: for attrib_name in ['requires', 'alias']: depname = typeinfo.elem.get(attrib_name) if depname: - self.gen.logMsg('diag', 'Generating dependent type', - depname, 'for', attrib_name, 'type', typename) # Do not recurse on self-referential structures. if typename != depname: self.markTypeRequired(depname, required) - else: - self.gen.logMsg('diag', 'type', typename, 'is self-referential') # Tag types used in defining this type (e.g. in nested # tags) # Look for in entire tree, # not just immediate children for subtype in typeinfo.elem.findall('.//type'): - self.gen.logMsg('diag', 'markRequired: type requires dependent ', subtype.text) if typename != subtype.text: self.markTypeRequired(subtype.text, required) - else: - self.gen.logMsg('diag', 'type', typename, 'is self-referential') # Tag enums used in defining this type, for example in # member[MEMBER_SIZE] for subenum in typeinfo.elem.findall('.//enum'): - self.gen.logMsg('diag', 'markRequired: type requires dependent ', subenum.text) self.markEnumRequired(subenum.text, required) # Tag type dependency in 'bitvalues' attributes as # required. This ensures that the bit values for a flag # are emitted depType = typeinfo.elem.get('bitvalues') if depType: - self.gen.logMsg('diag', 'Generating bitflag type', - depType, 'for type', typename) self.markTypeRequired(depType, required) group = self.lookupElementInfo(depType, self.groupdict) if group is not None: group.flagType = typeinfo typeinfo.required = required - elif '.h' not in typename: - self.gen.logMsg('warn', 'type:', typename, 'IS NOT DEFINED') def markEnumRequired(self, enumname, required): """Mark an enum as required or not. @@ -935,7 +533,6 @@ class Registry: - enumname - name of enum - required - boolean (to tag features as required or not)""" - self.gen.logMsg('diag', 'markEnumRequired: tagging enum:', enumname, '-> required =', required) enum = self.lookupElementInfo(enumname, self.enumdict) if enum is not None: # If the enum is part of a group, and is being removed, then @@ -945,7 +542,6 @@ class Registry: if not required: groupName = enum.elem.get('extends') if groupName is not None: - self.gen.logMsg('diag', f'markEnumRequired: Removing extending enum {enum.elem.get("name")}') # Look up the Info with matching groupName if groupName in self.groupdict: @@ -954,23 +550,12 @@ class Registry: if gienum is not None: # Remove copy of this enum from the group gi.elem.remove(gienum) - else: - self.gen.logMsg('warn', 'markEnumRequired: Cannot remove enum', - enumname, 'not found in group', - groupName) - else: - self.gen.logMsg('warn', 'markEnumRequired: Cannot remove enum', - enumname, 'from nonexistent group', - groupName) else: # This enum is not an extending enum. # The XML tree must be searched for all that # might have it, so we know the parent to delete from. enumName = enum.elem.get('name') - - self.gen.logMsg('diag', f'markEnumRequired: Removing non-extending enum {enumName}') - count = 0 for enums in self.reg.findall('enums'): for thisEnum in enums.findall('enum'): @@ -979,45 +564,20 @@ class Registry: count = count + 1 enums.remove(thisEnum) - if count == 0: - self.gen.logMsg('warn', f'markEnumRequired: {enumName}) not found in any tag') - enum.required = required # Tag enum dependencies in 'alias' attribute as required depname = enum.elem.get('alias') if depname: - self.gen.logMsg('diag', 'markEnumRequired: Generating dependent enum', - depname, 'for alias', enumname, 'required =', enum.required) self.markEnumRequired(depname, required) - else: - self.gen.logMsg('warn', f'markEnumRequired: {enumname} IS NOT DEFINED') def markCmdRequired(self, cmdname, required): """Mark a command as required or not. - cmdname - name of command - required - boolean (to tag features as required or not)""" - self.gen.logMsg('diag', 'tagging command:', cmdname, '-> required =', required) cmd = self.lookupElementInfo(cmdname, self.cmddict) if cmd is not None: cmd.required = required - - # Tag command dependencies in 'alias' attribute as required - # - # This is usually not done, because command 'aliases' are not - # actual C language aliases like type and enum aliases. Instead - # they are just duplicates of the function signature of the - # alias. This means that there is no dependency of a command - # alias on what it aliases. One exception is validity includes, - # where the spec markup needs the promoted-to validity include - # even if only the promoted-from command is being built. - if self.genOpts.requireCommandAliases: - depname = cmd.elem.get('alias') - if depname: - self.gen.logMsg('diag', 'Generating dependent command', - depname, 'for alias', cmdname) - self.markCmdRequired(depname, required) - # Tag all parameter types of this command as required. # This does not remove types of commands in a # tag, because many other commands may use the same type. @@ -1027,10 +587,7 @@ class Registry: # Look for in entire tree, # not just immediate children for type_elem in cmd.elem.findall('.//type'): - self.gen.logMsg('diag', 'markRequired: command implicitly requires dependent type', type_elem.text) self.markTypeRequired(type_elem.text, required) - else: - self.gen.logMsg('warn', 'command:', cmdname, 'IS NOT DEFINED') def markRequired(self, featurename, feature, required): """Require or remove features specified in the Element. @@ -1038,8 +595,6 @@ class Registry: - featurename - name of the feature - feature - Element for `` or `` tag - required - boolean (to tag features as required or not)""" - self.gen.logMsg('diag', 'markRequired (feature = , required =', required, ')') - # Loop over types, enums, and commands in the tag # @@ It would be possible to respect 'api' and 'profile' attributes # in individual features, but that is not done yet. @@ -1051,68 +606,14 @@ class Registry: for cmdElem in feature.findall('command'): self.markCmdRequired(cmdElem.get('name'), required) - # Extensions may need to extend existing commands or other items in the future. - # So, look for extend tags. - for extendElem in feature.findall('extend'): - extendType = extendElem.get('type') - if extendType == 'command': - commandName = extendElem.get('name') - successExtends = extendElem.get('successcodes') - if successExtends is not None: - for success in successExtends.split(','): - self.commandextensionsuccesses.append(self.commandextensiontuple(command=commandName, - value=success, - extension=featurename)) - errorExtends = extendElem.get('errorcodes') - if errorExtends is not None: - for error in errorExtends.split(','): - self.commandextensionerrors.append(self.commandextensiontuple(command=commandName, - value=error, - extension=featurename)) - else: - self.gen.logMsg('warn', 'extend type:', extendType, 'IS NOT SUPPORTED') - - def getAlias(self, elem, dict): - """Check for an alias in the same require block. - - - elem - Element to check for an alias""" - - # Try to find an alias - alias = elem.get('alias') - if alias is None: - name = elem.get('name') - typeinfo = self.lookupElementInfo(name, dict) - if not typeinfo: - self.gen.logMsg('error', name, 'is not a known name') - alias = typeinfo.elem.get('alias') - - return alias - - def checkForCorrectionAliases(self, alias, require, tag): - """Check for an alias in the same require block. - - - alias - String name of the alias - - require - `` block from the registry - - tag - tag to look for in the require block""" - - # For the time being, the code below is bypassed. It has the effect - # of excluding "spelling aliases" created to comply with the style - # guide, but this leaves references out of the specification and - # causes broken internal links. - # - # if alias and require.findall(tag + "[@name='" + alias + "']"): - # return True - - return False - - def fillFeatureDictionary(self, interface, featurename, api, profile): + def fillFeatureDictionary(self, interface, featurename, api): """Capture added interfaces for a `` or ``. - interface - Element for `` or ``, containing `` and `` tags - featurename - name of the feature - api - string specifying API name being generated - - profile - string specifying API profile being generated""" + """ # Explicitly initialize known types - errors for unhandled categories self.gen.featureDictionary[featurename] = { @@ -1129,71 +630,7 @@ class Registry: "funcpointer": {}, } - # marks things that are required by this version/profile - for require in interface.findall('require'): - if matchAPIProfile(api, profile, require): - - # Determine the required extension or version needed for a require block - # Assumes that only one of these is specified - # 'extension', and therefore 'required_key', may be a boolean - # expression of extension names. - # 'required_key' is used only as a dictionary key at - # present, and passed through to the script generators, so - # they must be prepared to parse that boolean expression. - required_key = require.get('depends') - - # Loop over types, enums, and commands in the tag - for typeElem in require.findall('type'): - typename = typeElem.get('name') - typeinfo = self.lookupElementInfo(typename, self.typedict) - - if typeinfo: - # Remove aliases in the same extension/feature; these are always added as a correction. Do not need the original to be visible. - alias = self.getAlias(typeElem, self.typedict) - if not self.checkForCorrectionAliases(alias, require, 'type'): - # Resolve the type info to the actual type, so we get an accurate read for 'structextends' - while alias: - typeinfo = self.lookupElementInfo(alias, self.typedict) - alias = typeinfo.elem.get('alias') - - typecat = typeinfo.elem.get('category') - typeextends = typeinfo.elem.get('structextends') - if not required_key in self.gen.featureDictionary[featurename][typecat]: - self.gen.featureDictionary[featurename][typecat][required_key] = {} - if not typeextends in self.gen.featureDictionary[featurename][typecat][required_key]: - self.gen.featureDictionary[featurename][typecat][required_key][typeextends] = [] - self.gen.featureDictionary[featurename][typecat][required_key][typeextends].append(typename) - else: - self.gen.logMsg('warn', 'fillFeatureDictionary: NOT filling for {}'.format(typename)) - - - for enumElem in require.findall('enum'): - enumname = enumElem.get('name') - typeinfo = self.lookupElementInfo(enumname, self.enumdict) - - # Remove aliases in the same extension/feature; these are always added as a correction. Do not need the original to be visible. - alias = self.getAlias(enumElem, self.enumdict) - if not self.checkForCorrectionAliases(alias, require, 'enum'): - enumextends = enumElem.get('extends') - if not required_key in self.gen.featureDictionary[featurename]['enumconstant']: - self.gen.featureDictionary[featurename]['enumconstant'][required_key] = {} - if not enumextends in self.gen.featureDictionary[featurename]['enumconstant'][required_key]: - self.gen.featureDictionary[featurename]['enumconstant'][required_key][enumextends] = [] - self.gen.featureDictionary[featurename]['enumconstant'][required_key][enumextends].append(enumname) - else: - self.gen.logMsg('warn', 'fillFeatureDictionary: NOT filling for {}'.format(typename)) - - for cmdElem in require.findall('command'): - # Remove aliases in the same extension/feature; these are always added as a correction. Do not need the original to be visible. - alias = self.getAlias(cmdElem, self.cmddict) - if not self.checkForCorrectionAliases(alias, require, 'command'): - if not required_key in self.gen.featureDictionary[featurename]['command']: - self.gen.featureDictionary[featurename]['command'][required_key] = [] - self.gen.featureDictionary[featurename]['command'][required_key].append(cmdElem.get('name')) - else: - self.gen.logMsg('warn', 'fillFeatureDictionary: NOT filling for {}'.format(typename)) - - def requireFeatures(self, interface, featurename, api, profile): + def requireFeatures(self, interface, featurename, api): """Process `` tags for a `` or ``. - interface - Element for `` or ``, containing @@ -1204,43 +641,9 @@ class Registry: # marks things that are required by this version/profile for feature in interface.findall('require'): - if matchAPIProfile(api, profile, feature): + if matchAPIProfile(api, feature): self.markRequired(featurename, feature, True) - def removeFeatures(self, interface, featurename, api, profile): - """Process `` tags for a `` or ``. - - - interface - Element for `` or ``, containing - `` tags - - featurename - name of the feature - - api - string specifying API name being generated - - profile - string specifying API profile being generated""" - - # marks things that are removed by this version/profile - for feature in interface.findall('remove'): - if matchAPIProfile(api, profile, feature): - self.markRequired(featurename, feature, False) - - def assignAdditionalValidity(self, interface, api, profile): - # Loop over all usage inside all tags. - for feature in interface.findall('require'): - if matchAPIProfile(api, profile, feature): - for v in feature.findall('usage'): - if v.get('command'): - self.cmddict[v.get('command')].additionalValidity.append(copy.deepcopy(v)) - if v.get('struct'): - self.typedict[v.get('struct')].additionalValidity.append(copy.deepcopy(v)) - - def removeAdditionalValidity(self, interface, api, profile): - # Loop over all usage inside all tags. - for feature in interface.findall('remove'): - if matchAPIProfile(api, profile, feature): - for v in feature.findall('usage'): - if v.get('command'): - self.cmddict[v.get('command')].removedValidity.append(copy.deepcopy(v)) - if v.get('struct'): - self.typedict[v.get('struct')].removedValidity.append(copy.deepcopy(v)) - def generateFeature(self, fname, ftype, dictionary, explicit=False): """Generate a single type / enum group / enum / command, and all its dependencies as needed. @@ -1252,34 +655,21 @@ class Registry: XML tag, False if it is a dependency of an explicit requirement.""" - self.gen.logMsg('diag', 'generateFeature: generating', ftype, fname) - - if not (explicit or self.genOpts.requireDepends): - self.gen.logMsg('diag', 'generateFeature: NOT generating', ftype, fname, 'because generator does not require dependencies') - return - f = self.lookupElementInfo(fname, dictionary) if f is None: - # No such feature. This is an error, but reported earlier - self.gen.logMsg('diag', 'No entry found for feature', fname, - 'returning!') + return + + if not f.required: return # If feature is not required, or has already been declared, return - if not f.required: - self.gen.logMsg('diag', 'Skipping', ftype, fname, '(not required)') - return if f.declared: - self.gen.logMsg('diag', 'Skipping', ftype, fname, '(already declared)') return # Always mark feature declared, as though actually emitted f.declared = True # Determine if this is an alias, and of what, if so alias = f.elem.get('alias') - if alias: - self.gen.logMsg('diag', fname, 'is an alias of', alias) - # Pull in dependent declaration(s) of the feature. # For types, there may be one type in the 'requires' attribute of # the element, one in the 'alias' attribute, and many in @@ -1297,8 +687,6 @@ class Registry: self.generateFeature(alias, 'type', self.typedict) requires = f.elem.get('requires') if requires: - self.gen.logMsg('diag', 'Generating required dependent type', - requires) self.generateFeature(requires, 'type', self.typedict) # Generate types used in defining this type (e.g. in nested @@ -1306,34 +694,23 @@ class Registry: # Look for in entire tree, # not just immediate children for subtype in f.elem.findall('.//type'): - self.gen.logMsg('diag', 'Generating required dependent ', - subtype.text) self.generateFeature(subtype.text, 'type', self.typedict) # Generate enums used in defining this type, for example in # member[MEMBER_SIZE] for subtype in f.elem.findall('.//enum'): - self.gen.logMsg('diag', 'Generating required dependent ', - subtype.text) self.generateFeature(subtype.text, 'enum', self.enumdict) # If the type is an enum group, look up the corresponding # group in the group dictionary and generate that instead. if f.elem.get('category') == 'enum': - self.gen.logMsg('diag', 'Type', fname, 'is an enum group, so generate that instead') group = self.lookupElementInfo(fname, self.groupdict) if alias is not None: - # An alias of another group name. - # Pass to genGroup with 'alias' parameter = aliased name - self.gen.logMsg('diag', 'Generating alias', fname, - 'for enumerated type', alias) # Now, pass the *aliased* GroupInfo to the genGroup, but # with an additional parameter which is the alias name. genProc = self.gen.genGroup f = self.lookupElementInfo(alias, self.groupdict) elif group is None: - self.gen.logMsg('warn', 'Skipping enum type', fname, - ': No matching enumerant group') return else: genProc = self.gen.genGroup @@ -1351,9 +728,6 @@ class Registry: # @ too. enums = group.elem.findall('enum') - - self.gen.logMsg('diag', 'generateFeature: checking enums for group', fname) - # Check for required enums, including aliases # LATER - Check for, report, and remove duplicates? enumAliases = [] @@ -1368,7 +742,7 @@ class Registry: # 'supported' attribute was injected when the element was # moved into the group in Registry.parseTree() supported_list = elem.get('supported').split(",") - if self.genOpts.defaultExtensions in supported_list: + if 'vulkan' in supported_list: required = True elif re.match(self.genOpts.addExtensions, extname) is not None: required = True @@ -1377,7 +751,6 @@ class Registry: else: required = True - self.gen.logMsg('diag', '* required =', required, 'for', name) if required: # Mark this element as required (in the element, not the EnumInfo) elem.set('required', 'true') @@ -1389,7 +762,6 @@ class Registry: name = elem.get('name') if name in enumAliases: elem.set('required', 'true') - self.gen.logMsg('diag', '* also need to require alias', name) if f is None: raise RuntimeError("Should not get here") if f.elem.get('category') == 'bitmask': @@ -1402,8 +774,6 @@ class Registry: genProc = self.gen.genCmd for type_elem in f.elem.findall('.//type'): depname = type_elem.text - self.gen.logMsg('diag', 'Generating required parameter type', - depname) self.generateFeature(depname, 'type', self.typedict) elif ftype == 'enum': # Generate enum dependencies in 'alias' attribute @@ -1413,17 +783,11 @@ class Registry: # Actually generate the type only if emitting declarations if self.emitFeatures: - self.gen.logMsg('diag', 'Emitting', ftype, 'decl for', fname) if genProc is None: raise RuntimeError("genProc is None when we should be emitting") genProc(f, fname, alias) - else: - self.gen.logMsg('diag', 'Skipping', ftype, fname, - '(should not be emitted)') if followupFeature: - self.gen.logMsg('diag', 'Generating required bitvalues ', - followupFeature) self.generateFeature(followupFeature, "type", self.typedict) def generateRequiredInterface(self, interface): @@ -1445,122 +809,10 @@ class Registry: for c in features.findall('command'): self.generateFeature(c.get('name'), 'command', self.cmddict, explicit=True) - def generateSpirv(self, spirv, dictionary): - if spirv is None: - self.gen.logMsg('diag', 'No entry found for element', name, - 'returning!') - return - - name = spirv.elem.get('name') - # No known alias for spirv elements - alias = None - if spirv.emit: - genProc = self.gen.genSpirv - genProc(spirv, name, alias) - - def stripUnsupportedAPIs(self, dictionary, attribute, supportedDictionary): - """Strip unsupported APIs from attributes of APIs. - dictionary - *Info dictionary of APIs to be updated - attribute - attribute name to look for in each API - supportedDictionary - dictionary in which to look for supported - API elements in the attribute""" - - for key in dictionary: - eleminfo = dictionary[key] - attribstring = eleminfo.elem.get(attribute) - if attribstring is not None: - apis = [] - stripped = False - for api in attribstring.split(','): - ##print('Checking API {} referenced by {}'.format(api, key)) - if api in supportedDictionary and supportedDictionary[api].required: - apis.append(api) - else: - stripped = True - ##print('\t**STRIPPING API {} from {}'.format(api, key)) - - # Update the attribute after stripping stuff. - # Could sort apis before joining, but it is not a clear win - if stripped: - eleminfo.elem.set(attribute, ','.join(apis)) - - def stripUnsupportedAPIsFromList(self, dictionary, supportedDictionary): - """Strip unsupported APIs from attributes of APIs. - dictionary - dictionary of list of structure name strings - supportedDictionary - dictionary in which to look for supported - API elements in the attribute""" - - for key in dictionary: - attribstring = dictionary[key] - if attribstring is not None: - apis = [] - stripped = False - for api in attribstring: - ##print('Checking API {} referenced by {}'.format(api, key)) - if supportedDictionary[api].required: - apis.append(api) - else: - stripped = True - ##print('\t**STRIPPING API {} from {}'.format(api, key)) - - # Update the attribute after stripping stuff. - # Could sort apis before joining, but it is not a clear win - if stripped: - dictionary[key] = apis - - def generateFormat(self, format, dictionary): - if format is None: - self.gen.logMsg('diag', 'No entry found for format element', - 'returning!') - return - - name = format.elem.get('name') - # No known alias for VkFormat elements - alias = None - if format.emit: - genProc = self.gen.genFormat - genProc(format, name, alias) - - def generateSyncStage(self, sync): - genProc = self.gen.genSyncStage - genProc(sync) - - def generateSyncAccess(self, sync): - genProc = self.gen.genSyncAccess - genProc(sync) - - def generateSyncPipeline(self, sync): - genProc = self.gen.genSyncPipeline - genProc(sync) - - def tagValidExtensionStructs(self): - """Construct a "validextensionstructs" list for parent structures - based on "structextends" tags in child structures. - Only do this for structures tagged as required.""" - - for typeinfo in self.typedict.values(): - type_elem = typeinfo.elem - if typeinfo.required and type_elem.get('category') == 'struct': - struct_extends = type_elem.get('structextends') - if struct_extends is not None: - for parent in struct_extends.split(','): - # self.gen.logMsg('diag', type_elem.get('name'), 'extends', parent) - self.validextensionstructs[parent].append(type_elem.get('name')) - - # Sort the lists so they do not depend on the XML order - for parent in self.validextensionstructs: - self.validextensionstructs[parent].sort() - def apiGen(self): """Generate interface for specified versions using the current generator and generator options""" - self.gen.logMsg('diag', '*******************************************') - self.gen.logMsg('diag', ' Registry.apiGen file:', self.genOpts.filename, - 'api:', self.genOpts.apiname, - 'profile:', self.genOpts.profile) - self.gen.logMsg('diag', '*******************************************') - # Could reset required/declared flags for all features here. # This has been removed as never used. The initial motivation was # the idea of calling apiGen() repeatedly for different targets, but @@ -1573,10 +825,7 @@ class Registry: regVersions = re.compile(self.genOpts.versions) regEmitVersions = re.compile(self.genOpts.emitversions) regAddExtensions = re.compile(self.genOpts.addExtensions) - regRemoveExtensions = re.compile(self.genOpts.removeExtensions) regEmitExtensions = re.compile(self.genOpts.emitExtensions) - regEmitSpirv = re.compile(self.genOpts.emitSpirv) - regEmitFormats = re.compile(self.genOpts.emitFormats) # Get all matching API feature names & add to list of FeatureInfo # Note we used to select on feature version attributes, not names. @@ -1585,7 +834,7 @@ class Registry: for key in self.apidict: fi = self.apidict[key] api = fi.elem.get('api') - if apiNameMatch(self.genOpts.apiname, api): + if apiNameMatch('vulkan', api): apiMatch = True if regVersions.match(fi.name): # Matches API & version #s being generated. Mark for @@ -1593,24 +842,6 @@ class Registry: # @@ Could use 'declared' instead of 'emit'? fi.emit = (regEmitVersions.match(fi.name) is not None) features.append(fi) - if not fi.emit: - self.gen.logMsg('diag', 'NOT tagging feature api =', api, - 'name =', fi.name, 'version =', fi.version, - 'for emission (does not match emitversions pattern)') - else: - self.gen.logMsg('diag', 'Including feature api =', api, - 'name =', fi.name, 'version =', fi.version, - 'for emission (matches emitversions pattern)') - else: - self.gen.logMsg('diag', 'NOT including feature api =', api, - 'name =', fi.name, 'version =', fi.version, - '(does not match requested versions)') - else: - self.gen.logMsg('diag', 'NOT including feature api =', api, - 'name =', fi.name, - '(does not match requested API)') - if not apiMatch: - self.gen.logMsg('warn', 'No matching API versions found!') # Get all matching extensions, in order by their extension number, # and add to the list of features. @@ -1624,10 +855,7 @@ class Registry: # Include extension if defaultExtensions is not None and is # exactly matched by the 'supported' attribute. - if apiNameMatch(self.genOpts.defaultExtensions, - ei.elem.get('supported')): - self.gen.logMsg('diag', 'Including extension', - extName, "(defaultExtensions matches the 'supported' attribute)") + if apiNameMatch('vulkan', ei.elem.get('supported')): include = True # Include additional extensions if the extension name matches @@ -1636,60 +864,15 @@ class Registry: # tagged appropriately in the registry. # However, we still respect the 'supported' attribute. if regAddExtensions.match(extName) is not None: - if not apiNameMatch(self.genOpts.apiname, ei.elem.get('supported')): - self.gen.logMsg('diag', 'NOT including extension', - extName, '(matches explicitly requested, but does not match the \'supported\' attribute)') + if not apiNameMatch('vulkan', ei.elem.get('supported')): include = False else: - self.gen.logMsg('diag', 'Including extension', - extName, '(matches explicitly requested extensions to add)') include = True - # Remove extensions if the name matches the regexp specified - # in generator options. This allows forcing removal of - # extensions from an interface even if they are tagged that - # way in the registry. - if regRemoveExtensions.match(extName) is not None: - self.gen.logMsg('diag', 'Removing extension', - extName, '(matches explicitly requested extensions to remove)') - include = False - # If the extension is to be included, add it to the # extension features list. if include: ei.emit = (regEmitExtensions.match(extName) is not None) features.append(ei) - if not ei.emit: - self.gen.logMsg('diag', 'NOT tagging extension', - extName, - 'for emission (does not match emitextensions pattern)') - - # Hack - can be removed when validity generator goes away - # (Jon) I am not sure what this does, or if it should - # respect the ei.emit flag above. - self.requiredextensions.append(extName) - else: - self.gen.logMsg('diag', 'NOT including extension', - extName, '(does not match api attribute or explicitly requested extensions)') - - # Add all spirv elements to list - # generators decide to emit them all or not - # Currently no filtering as no client of these elements needs filtering - spirvexts = [] - for key in self.spirvextdict: - si = self.spirvextdict[key] - si.emit = (regEmitSpirv.match(key) is not None) - spirvexts.append(si) - spirvcaps = [] - for key in self.spirvcapdict: - si = self.spirvcapdict[key] - si.emit = (regEmitSpirv.match(key) is not None) - spirvcaps.append(si) - - formats = [] - for key in self.formatsdict: - si = self.formatsdict[key] - si.emit = (regEmitFormats.match(key) is not None) - formats.append(si) # Sort the features list, if a sort procedure is defined if self.genOpts.sortProcedure: @@ -1702,29 +885,9 @@ class Registry: # If a profile other than 'None' is being generated, it must # match the profile attribute (if any) of the and # tags. - self.gen.logMsg('diag', 'PASS 1: TAG FEATURES') for f in features: - self.gen.logMsg('diag', 'PASS 1: Tagging required and features for', f.name) - self.fillFeatureDictionary(f.elem, f.name, self.genOpts.apiname, self.genOpts.profile) - self.requireFeatures(f.elem, f.name, self.genOpts.apiname, self.genOpts.profile) - self.assignAdditionalValidity(f.elem, self.genOpts.apiname, self.genOpts.profile) - - for f in features: - self.gen.logMsg('diag', 'PASS 2: Tagging removed features for', f.name) - self.removeFeatures(f.elem, f.name, self.genOpts.apiname, self.genOpts.profile) - self.removeAdditionalValidity(f.elem, self.genOpts.apiname, self.genOpts.profile) - - # Now, strip references to APIs that are not required. - # At present such references may occur in: - # Structs in 'structextends' attributes - # Enums in 'successcodes' and 'errorcodes' attributes - self.stripUnsupportedAPIs(self.typedict, 'structextends', self.typedict) - self.stripUnsupportedAPIs(self.cmddict, 'successcodes', self.enumdict) - self.stripUnsupportedAPIs(self.cmddict, 'errorcodes', self.enumdict) - self.stripUnsupportedAPIsFromList(self.validextensionstructs, self.typedict) - - # Construct lists of valid extension structures - self.tagValidExtensionStructs() + self.fillFeatureDictionary(f.elem, f.name, 'vulkan') + self.requireFeatures(f.elem, f.name, 'vulkan') # @@May need to strip / # tags of these forms: @@ -1736,44 +899,12 @@ class Registry: # Pass 3: loop over specified API versions and extensions printing # declarations for required things which have not already been # generated. - self.gen.logMsg('diag', 'PASS 3: GENERATE INTERFACES FOR FEATURES') self.gen.beginFile(self.genOpts) for f in features: - self.gen.logMsg('diag', 'PASS 3: Generating interface for', - f.name) emit = self.emitFeatures = f.emit - if not emit: - self.gen.logMsg('diag', 'PASS 3: NOT declaring feature', - f.elem.get('name'), 'because it is not tagged for emission') # Generate the interface (or just tag its elements as having been # emitted, if they have not been). self.gen.beginFeature(f.elem, emit) self.generateRequiredInterface(f.elem) self.gen.endFeature() - # Generate spirv elements - for s in spirvexts: - self.generateSpirv(s, self.spirvextdict) - for s in spirvcaps: - self.generateSpirv(s, self.spirvcapdict) - for s in formats: - self.generateFormat(s, self.formatsdict) - for s in self.syncstagedict: - self.generateSyncStage(self.syncstagedict[s]) - for s in self.syncaccessdict: - self.generateSyncAccess(self.syncaccessdict[s]) - for s in self.syncpipelinedict: - self.generateSyncPipeline(self.syncpipelinedict[s]) self.gen.endFile() - - def apiReset(self): - """Reset type/enum/command dictionaries before generating another API. - - Use between apiGen() calls to reset internal state.""" - for datatype in self.typedict: - self.typedict[datatype].resetState() - for enum in self.enumdict: - self.enumdict[enum].resetState() - for cmd in self.cmddict: - self.cmddict[cmd].resetState() - for cmd in self.apidict: - self.apidict[cmd].resetState()