layers: 1.0.7 update for XML Registry files

Update local copies of generator.py, genvk.py, and vk.xml with the
latest versions from Vulkan-Docs.

Change-Id: Id2a3da34374fb1c39532cd3c124461eb953f6b89
diff --git a/generator.py b/generator.py
index cab2696..023dd10 100644
--- a/generator.py
+++ b/generator.py
@@ -1,4 +1,26 @@
 #!/usr/bin/python3 -i
+#
+# Copyright (c) 2013-2016 The Khronos Group Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and/or associated documentation files (the
+# "Materials"), to deal in the Materials without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Materials, and to
+# permit persons to whom the Materials are furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Materials.
+#
+# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
 import os,re,sys
 from collections import namedtuple
 from lxml import etree
@@ -750,7 +772,8 @@
         #
         # Multiple inclusion protection & C++ wrappers.
         if (genOpts.protectFile and self.genOpts.filename):
-            headerSym = '__' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename))
+            headerSym = re.sub('\.h', '_h_',
+                               os.path.basename(self.genOpts.filename)).upper()
             write('#ifndef', headerSym, file=self.outFile)
             write('#define', headerSym, '1', file=self.outFile)
             self.newline()
@@ -889,13 +912,22 @@
     def genGroup(self, groupinfo, groupName):
         OutputGenerator.genGroup(self, groupinfo, groupName)
         groupElem = groupinfo.elem
-        # See if this group needs min/max/num/padding at end
-        expand = 'expand' in groupElem.keys()
-        if (expand):
-            expandPrefix = groupElem.get('expand')
+
+        expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^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 = "\ntypedef enum " + groupName + " {\n"
 
+        isEnum = ('FLAG_BITS' not in expandPrefix)
+
         # 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
@@ -907,8 +939,15 @@
             # Should catch exceptions here for more complex constructs. Not yet.
             (numVal,strVal) = self.enumToValue(elem, True)
             name = elem.get('name')
-            body += "    " + name + " = " + strVal + ",\n"
-            if (expand and elem.get('extends') is None):
+
+            # Extension enumerants are only included if they are requested
+            # in addExtensions or match defaultExtensions.
+            if (elem.get('extname') is None or
+              re.match(self.genOpts.addExtensions,elem.get('extname')) is not None or
+              self.genOpts.defaultExtensions == elem.get('supported')):
+                body += "    " + name + " = " + strVal + ",\n"
+
+            if (isEnum  and elem.get('extends') is None):
                 if (minName == None):
                     minName = maxName = name
                     minValue = maxValue = numVal
@@ -920,11 +959,13 @@
                     maxValue = numVal
         # Generate min/max value tokens and a range-padding enum. Need some
         # additional padding to generate correct names...
-        if (expand):
-            body += "    " + expandPrefix + "_BEGIN_RANGE = " + minName + ",\n"
-            body += "    " + expandPrefix + "_END_RANGE = " + maxName + ",\n"
-            body += "    " + expandPrefix + "_RANGE_SIZE = (" + maxName + " - " + minName + " + 1),\n"
-            body += "    " + expandPrefix + "_MAX_ENUM = 0x7FFFFFFF\n"
+        if isEnum:
+            body += "    " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n"
+            body += "    " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n"
+            body += "    " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n"
+
+        body += "    " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
+
         # Postfix
         body += "} " + groupName + ";"
         if groupElem.get('type') == 'bitmask':
@@ -1000,8 +1041,14 @@
         self.logMsg('diag', '# Generating include file:', filename)
         fp = open(filename, 'w')
         # Asciidoc anchor
+        write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
+        write('ifndef::doctype-manpage[]', file=fp)
         write('[[{0},{0}]]'.format(basename), file=fp)
         write('["source","{basebackend@docbook:c++:cpp}",title=""]', file=fp)
+        write('endif::doctype-manpage[]', file=fp)
+        write('ifdef::doctype-manpage[]', file=fp)
+        write('["source","{basebackend@docbook:c++:cpp}"]', file=fp)
+        write('endif::doctype-manpage[]', file=fp)
         write('------------------------------------------------------------------------------', file=fp)
         write(contents, file=fp)
         write('------------------------------------------------------------------------------', file=fp)
@@ -1064,10 +1111,24 @@
     def genGroup(self, groupinfo, groupName):
         OutputGenerator.genGroup(self, groupinfo, groupName)
         groupElem = groupinfo.elem
-        # See if this group needs min/max/num/padding at end
-        expand = self.genOpts.expandEnumerants and ('expand' in groupElem.keys())
-        if (expand):
-            expandPrefix = groupElem.get('expand')
+
+        # See if we need min/max/num/padding at end
+        expand = self.genOpts.expandEnumerants
+
+        if expand:
+            expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
+            isEnum = ('FLAG_BITS' not in expandName)
+
+            expandPrefix = expandName
+            expandSuffix = ''
+
+            # Look for a suffix
+            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
         s = "typedef enum " + groupName + " {\n"
 
@@ -1080,8 +1141,15 @@
             # Should catch exceptions here for more complex constructs. Not yet.
             (numVal,strVal) = self.enumToValue(elem, True)
             name = elem.get('name')
-            s += "    " + name + " = " + strVal + ",\n"
-            if (expand and elem.get('extends') is None):
+
+            # Extension enumerants are only included if they are requested
+            # in addExtensions or match defaultExtensions.
+            if (elem.get('extname') is None or
+              re.match(self.genOpts.addExtensions,elem.get('extname')) is not None or
+              self.genOpts.defaultExtensions == elem.get('supported')):
+                s += "    " + name + " = " + strVal + ",\n"
+
+            if (expand and isEnum and elem.get('extends') is None):
                 if (minName == None):
                     minName = maxName = name
                     minValue = maxValue = numVal
@@ -1095,10 +1163,12 @@
         # additional padding to generate correct names...
         if (expand):
             s += "\n"
-            s += "    " + expandPrefix + "_BEGIN_RANGE = " + minName + ",\n"
-            s += "    " + expandPrefix + "_END_RANGE = " + maxName + ",\n"
-            s += "    " + expandPrefix + "_NUM = (" + maxName + " - " + minName + " + 1),\n"
-            s += "    " + expandPrefix + "_MAX_ENUM = 0x7FFFFFFF\n"
+            if isEnum:
+                s += "    " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n"
+                s += "    " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n"
+                s += "    " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n"
+
+            s += "    " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
         # Postfix
         s += "} " + groupName + ";"
         self.writeInclude('enums', groupName, s)
@@ -1319,46 +1389,89 @@
         self.logMsg('diag', '# Generating include file:', filename)
         fp = open(filename, 'w')
         # Asciidoc anchor
+        write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
 
         # Valid Usage
         if validity is not None:
+            write('ifndef::doctype-manpage[]', file=fp)
             write('.Valid Usage', file=fp)
             write('*' * 80, file=fp)
+            write('endif::doctype-manpage[]', file=fp)
+            write('ifdef::doctype-manpage[]', file=fp)
+            write('Valid Usage', file=fp)
+            write('-----------', file=fp)
+            write('endif::doctype-manpage[]', file=fp)
             write(validity, file=fp, end='')
+            write('ifndef::doctype-manpage[]', file=fp)
             write('*' * 80, file=fp)
+            write('endif::doctype-manpage[]', file=fp)
             write('', file=fp)
 
         # Host Synchronization
         if threadsafety is not None:
+            write('ifndef::doctype-manpage[]', file=fp)
             write('.Host Synchronization', file=fp)
             write('*' * 80, file=fp)
+            write('endif::doctype-manpage[]', file=fp)
+            write('ifdef::doctype-manpage[]', file=fp)
+            write('Host Synchronization', file=fp)
+            write('--------------------', file=fp)
+            write('endif::doctype-manpage[]', file=fp)
             write(threadsafety, file=fp, end='')
+            write('ifndef::doctype-manpage[]', file=fp)
             write('*' * 80, file=fp)
+            write('endif::doctype-manpage[]', file=fp)
             write('', file=fp)
 
         # Command Properties - contained within a block, to avoid table numbering
         if commandpropertiesentry is not None:
+            write('ifndef::doctype-manpage[]', file=fp)
             write('.Command Properties', file=fp)
             write('*' * 80, file=fp)
+            write('endif::doctype-manpage[]', file=fp)
+            write('ifdef::doctype-manpage[]', file=fp)
+            write('Command Properties', file=fp)
+            write('------------------', file=fp)
+            write('endif::doctype-manpage[]', file=fp)
             write('[options="header", width="100%"]', file=fp)
             write('|=====================', file=fp)
             write('|Command Buffer Levels|Render Pass Scope|Supported Queue Types', file=fp)
             write(commandpropertiesentry, file=fp)
             write('|=====================', file=fp)
+            write('ifndef::doctype-manpage[]', file=fp)
             write('*' * 80, file=fp)
+            write('endif::doctype-manpage[]', file=fp)
             write('', file=fp)
 
         # Success Codes - contained within a block, to avoid table numbering
         if successcodes is not None or errorcodes is not None:
+            write('ifndef::doctype-manpage[]', file=fp)
             write('.Return Codes', file=fp)
             write('*' * 80, file=fp)
+            write('endif::doctype-manpage[]', file=fp)
+            write('ifdef::doctype-manpage[]', file=fp)
+            write('Return Codes', file=fp)
+            write('------------', file=fp)
+            write('endif::doctype-manpage[]', file=fp)
             if successcodes is not None:
+                write('ifndef::doctype-manpage[]', file=fp)
                 write('<<fundamentals-successcodes,Success>>::', file=fp)
+                write('endif::doctype-manpage[]', file=fp)
+                write('ifdef::doctype-manpage[]', file=fp)
+                write('On success, this command returns::', file=fp)
+                write('endif::doctype-manpage[]', file=fp)
                 write(successcodes, file=fp)
             if errorcodes is not None:
+                write('ifndef::doctype-manpage[]', file=fp)
                 write('<<fundamentals-errorcodes,Failure>>::', file=fp)
+                write('endif::doctype-manpage[]', file=fp)
+                write('ifdef::doctype-manpage[]', file=fp)
+                write('On failure, this command returns::', file=fp)
+                write('endif::doctype-manpage[]', file=fp)
                 write(errorcodes, file=fp)
+            write('ifndef::doctype-manpage[]', file=fp)
             write('*' * 80, file=fp)
+            write('endif::doctype-manpage[]', file=fp)
             write('', file=fp)
 
         fp.close()
@@ -1952,13 +2065,9 @@
 
                         if self.paramIsPointer(param):
                             asciidoc += 'the value referenced by '
-                        else:
-                            asciidoc += 'the value of '
 
                     elif self.paramIsPointer(param):
                         asciidoc += 'The value referenced by '
-                    else:
-                        asciidoc += 'The value of '
 
                     asciidoc += self.makeParameterName(arraylength)
                     asciidoc += ' must: be greater than `0`'
@@ -2003,10 +2112,10 @@
                 # Capitalize and add to the main language
                 asciidoc += parentlanguage
 
-        # Add in any plain-text validation language that's in the xml
+        # Add in any plain-text validation language that should be added
         for usage in usages:
             asciidoc += '* '
-            asciidoc += usage.text
+            asciidoc += usage
             asciidoc += '\n'
 
         # In case there's nothing to report, return None
@@ -2094,7 +2203,7 @@
 
             successcodeentry = ''
             successcodes = successcodes.split(',')
-            return '* ' + '\n* '.join(successcodes)
+            return '* ename:' + '\n* ename:'.join(successcodes)
 
         return None
 
@@ -2105,7 +2214,7 @@
 
             errorcodeentry = ''
             errorcodes = errorcodes.split(',')
-            return '* ' + '\n* '.join(errorcodes)
+            return '* ename:' + '\n* ename:'.join(errorcodes)
 
         return None
 
@@ -2114,9 +2223,17 @@
     def genCmd(self, cmdinfo, name):
         OutputGenerator.genCmd(self, cmdinfo, name)
         #
-        # Get all thh parameters
+        # Get all the parameters
         params = cmdinfo.elem.findall('param')
-        usages = cmdinfo.elem.findall('validity/usage')
+        usageelements = cmdinfo.elem.findall('validity/usage')
+        usages = []
+
+        for usage in usageelements:
+            usages.append(usage.text)
+        for usage in cmdinfo.additionalValidity:
+            usages.append(usage.text)
+        for usage in cmdinfo.removedValidity:
+            usages.remove(usage.text)
 
         validity = self.makeValidUsageStatements(cmdinfo.elem, name, params, usages)
         threadsafety = self.makeThreadSafetyBlock(cmdinfo.elem, 'param')
@@ -2134,7 +2251,16 @@
         # Anything that's only ever returned can't be set by the user, so shouldn't have any validity information.
         if typeinfo.elem.attrib.get('returnedonly') is None:
             params = typeinfo.elem.findall('member')
-            usages = typeinfo.elem.findall('validity/usage')
+
+            usageelements = typeinfo.elem.findall('validity/usage')
+            usages = []
+
+            for usage in usageelements:
+                usages.append(usage.text)
+            for usage in typeinfo.additionalValidity:
+                usages.append(usage.text)
+            for usage in typeinfo.removedValidity:
+                usages.remove(usage.text)
 
             validity = self.makeValidUsageStatements(typeinfo.elem, typename, params, usages)
             threadsafety = self.makeThreadSafetyBlock(typeinfo.elem, 'member')
@@ -2194,6 +2320,7 @@
             fp = open(filename, 'w')
 
             # Host Synchronization
+            write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
             write('.Externally Synchronized Parameters', file=fp)
             write('*' * 80, file=fp)
             write(self.threadsafety['parameters'], file=fp, end='')
@@ -2207,6 +2334,7 @@
             fp = open(filename, 'w')
 
             # Host Synchronization
+            write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
             write('.Externally Synchronized Parameter Lists', file=fp)
             write('*' * 80, file=fp)
             write(self.threadsafety['parameterlists'], file=fp, end='')
@@ -2220,6 +2348,7 @@
             fp = open(filename, 'w')
 
             # Host Synchronization
+            write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
             write('.Implicit Externally Synchronized Parameters', file=fp)
             write('*' * 80, file=fp)
             write(self.threadsafety['implicit'], file=fp, end='')
@@ -3014,6 +3143,25 @@
                 return param
         return None
     #
+    # Extract length values from latexmath.  Currently an inflexible solution that looks for specific
+    # patterns that are found in vk.xml.  Will need to be updated when new patterns are introduced.
+    def parseLateXMath(self, source):
+        name = 'ERROR'
+        decoratedName = 'ERROR'
+        if 'mathit' in source:
+            # Matches expressions similar to 'latexmath:[$\lceil{\mathit{rasterizationSamples} \over 32}\rceil$]'
+            match = re.match(r'latexmath\s*\:\s*\[\s*\$\\l(\w+)\s*\{\s*\\mathit\s*\{\s*(\w+)\s*\}\s*\\over\s*(\d+)\s*\}\s*\\r(\w+)\$\s*\]', source)
+            if not match or match.group(1) != match.group(4):
+                raise 'Unrecognized latexmath expression'
+            name = match.group(2)
+            decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
+        else:
+            # Matches expressions similar to 'latexmath : [$dataSize \over 4$]'
+            match = re.match(r'latexmath\s*\:\s*\[\s*\$\s*(\w+)\s*\\over\s*(\d+)\s*\$\s*\]', source)
+            name = match.group(1)
+            decoratedName = '{}/{}'.format(*match.group(1, 2))
+        return name, decoratedName
+    #
     # Get the length paramater record for the specified parameter name
     def getLenParam(self, params, name):
         lenParam = None
@@ -3022,11 +3170,14 @@
                 # The count is obtained by dereferencing a member of a struct parameter
                 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isoptional=False, type=None, len=None, isstaticarray=None, extstructs=None, cdecl=None)
             elif 'latexmath' in name:
-                result = re.search('mathit\{(\w+)\}', name)
-                lenParam = self.getParamByName(params, result.group(1))
-            elif '/' in name:
-                # Len specified as an equation such as dataSize/4
-                lenParam = self.getParamByName(params, name.split('/')[0])
+                lenName, decoratedName = self.parseLateXMath(name)
+                lenParam = self.getParamByName(params, lenName)
+                # TODO: Zero-check the result produced by the equation?
+                # Copy the stored len parameter entry and overwrite the name with the processed latexmath equation
+                #param = self.getParamByName(params, lenName)
+                #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer,
+                #                             isoptional=param.isoptional, type=param.type, len=param.len,
+                #                             isstaticarray=param.isstaticarray, extstructs=param.extstructs, cdecl=param.cdecl)
             else:
                 lenParam = self.getParamByName(params, name)
         return lenParam