scripts: Optimize expensive xml lookups

4x speedup in codegen by fetching type information ahead of time instead
of doing repeated ad-hoc queries on the whole tree

Change-Id: Iba5625b830aa09baaf21f1eb16957d2f67d5d24a
diff --git a/scripts/common_codegen.py b/scripts/common_codegen.py
index d9fcbed..4adb329 100644
--- a/scripts/common_codegen.py
+++ b/scripts/common_codegen.py
@@ -21,7 +21,7 @@
 import os,re,sys,string
 import xml.etree.ElementTree as etree
 from generator import *
-from collections import namedtuple
+from collections import namedtuple, OrderedDict
 
 # Copyright text prefixing all headers (list of strings).
 prefixStrings = [
@@ -72,13 +72,24 @@
         protect = platform_dict[platform]
     return protect
 
-# Check if an object is a non-dispatchable handle
-def IsHandleTypeNonDispatchable(tree, handletype):
-    handle = tree.find("types/type/[name='" + handletype + "'][@category='handle']")
-    if handle is not None and handle.find('type').text == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE':
-        return True
-    else:
-        return False
+# Return a dict containing the dispatchable/non-dispatchable type of every handle
+def GetHandleTypes(tree):
+    handles = OrderedDict()
+    for elem in tree.findall("types/type/[@category='handle']"):
+        if not elem.get('alias'):
+            name = elem.get('name')
+            handles[name] = elem.find('type').text
+    return handles
+
+# Return a dict containing the category attribute of every type
+def GetTypeCategories(tree):
+    type_categories = OrderedDict()
+    for elem in tree.findall("types/type"):
+        if not elem.get('alias'):
+            # name is either an attribute or the text of a child <name> tag
+            name = elem.get('name') or (elem.find("name") and elem.find('name').text)
+            type_categories[name] = elem.get('category')
+    return type_categories
 
 # Treats outdents a multiline string by the leading whitespace on the first line
 # Optionally indenting by the given prefix
diff --git a/scripts/dispatch_table_helper_generator.py b/scripts/dispatch_table_helper_generator.py
index 0fb8f53..8d202c9 100644
--- a/scripts/dispatch_table_helper_generator.py
+++ b/scripts/dispatch_table_helper_generator.py
@@ -79,6 +79,10 @@
     # Called once at the beginning of each run
     def beginFile(self, genOpts):
         OutputGenerator.beginFile(self, genOpts)
+        
+        # Initialize members that require the tree
+        self.handle_types = GetHandleTypes(self.registry.tree)
+
         write("#pragma once", file=self.outFile)
         # User-supplied prefix text, if any (list of strings)
         if (genOpts.prefixText):
@@ -168,8 +172,7 @@
     #
     # Determine if this API should be ignored or added to the instance or device dispatch table
     def AddCommandToDispatchList(self, name, handle_type, protect, cmdinfo):
-        handle = self.registry.tree.find("types/type/[name='" + handle_type + "'][@category='handle']")
-        if handle is None:
+        if handle_type not in self.handle_types:
             return
         if handle_type != 'VkInstance' and handle_type != 'VkPhysicalDevice' and name != 'vkGetInstanceProcAddr':
             self.device_dispatch_list.append((name, self.featureExtraProtect))
diff --git a/scripts/helper_file_generator.py b/scripts/helper_file_generator.py
index 9a8b60f..4e2aa47 100644
--- a/scripts/helper_file_generator.py
+++ b/scripts/helper_file_generator.py
@@ -106,6 +106,8 @@
     # Called once at the beginning of each run
     def beginFile(self, genOpts):
         OutputGenerator.beginFile(self, genOpts)
+        # Initialize members that require the tree
+        self.handle_types = GetHandleTypes(self.registry.tree)
         # User-supplied prefix text, if any (list of strings)
         self.helper_file_type = genOpts.helper_file_type
         self.library_name = genOpts.library_name
@@ -305,16 +307,14 @@
             type_key = 'VK_DEFINE_HANDLE'
         else:
             type_key = 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'
-        handle = self.registry.tree.find("types/type/[name='" + handle_type + "'][@category='handle']")
-        if handle is not None and handle.find('type').text == type_key:
+        if self.handle_types.get(handle_type) == type_key:
             return True
         # if handle_type is a struct, search its members
         if handle_type in self.structNames:
             member_index = next((i for i, v in enumerate(self.structMembers) if v[0] == handle_type), None)
             if member_index is not None:
                 for item in self.structMembers[member_index].members:
-                    handle = self.registry.tree.find("types/type/[name='" + item.type + "'][@category='handle']")
-                    if handle is not None and handle.find('type').text == type_key:
+                    if self.handle_types.get(item.type) == type_key:
                         return True
         return False
     #
@@ -687,7 +687,7 @@
             type_list.append(enum_entry)
             object_type_info[enum_entry] = { 'VkType': item }
             # We'll want lists of the dispatchable and non dispatchable handles below with access to the same info
-            if IsHandleTypeNonDispatchable(self.registry.tree, item) :
+            if self.handle_types.get(item) == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE':
                 non_dispatchable[item] = enum_entry
             else:
                 dispatchable[item] = enum_entry
diff --git a/scripts/layer_chassis_dispatch_generator.py b/scripts/layer_chassis_dispatch_generator.py
index 481a214..59875d5 100644
--- a/scripts/layer_chassis_dispatch_generator.py
+++ b/scripts/layer_chassis_dispatch_generator.py
@@ -1083,6 +1083,10 @@
     #
     def beginFile(self, genOpts):
         OutputGenerator.beginFile(self, genOpts)
+        # Initialize members that require the tree
+        self.handle_types = GetHandleTypes(self.registry.tree)
+        self.type_categories = GetTypeCategories(self.registry.tree)
+        # Output Copyright
         self.appendSection('header_file', self.inline_copyright_message)
         # Multiple inclusion protection & C++ namespace.
         self.header = False
@@ -1179,20 +1183,9 @@
                 ispointer = True
         return ispointer
     #
-    # Get the category of a type
-    def getTypeCategory(self, typename):
-        types = self.registry.tree.findall("types/type")
-        for elem in types:
-            if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
-                return elem.attrib.get('category')
-    #
     # Check if a parent object is dispatchable or not
     def isHandleTypeNonDispatchable(self, handletype):
-        handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']")
-        if handle is not None and handle.find('type').text == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE':
-            return True
-        else:
-            return False
+        return self.handle_types.get(handletype) == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'
     #
     # Retrieve the type and name for a parameter
     def getTypeNameTuple(self, param):
@@ -1304,7 +1297,7 @@
         struct_list = set()
         for item in item_list:
             paramtype = item.find('type')
-            typecategory = self.getTypeCategory(paramtype.text)
+            typecategory = self.type_categories[paramtype.text]
             if typecategory == 'struct':
                 if self.struct_contains_ndo(paramtype.text) == True:
                     struct_list.add(item)
diff --git a/scripts/layer_chassis_generator.py b/scripts/layer_chassis_generator.py
index 82a2486..ca9c31b 100644
--- a/scripts/layer_chassis_generator.py
+++ b/scripts/layer_chassis_generator.py
@@ -1502,23 +1502,17 @@
 
     # Check if an object is a non-dispatchable handle
     def isHandleTypeNonDispatchable(self, handletype):
-        handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']")
-        if handle is not None and handle.find('type').text == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE':
-            return True
-        else:
-            return False
+        return self.handle_types.get(handletype) == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'
 
     # Check if an object is a dispatchable handle
     def isHandleTypeDispatchable(self, handletype):
-        handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']")
-        if handle is not None and handle.find('type').text == 'VK_DEFINE_HANDLE':
-            return True
-        else:
-            return False
+        return self.handle_types.get(handletype) == 'VK_DEFINE_HANDLE'
     #
     #
     def beginFile(self, genOpts):
         OutputGenerator.beginFile(self, genOpts)
+        # Initialize members that require the tree
+        self.handle_types = GetHandleTypes(self.registry.tree)
         # Output Copyright
         write(self.inline_copyright_message, file=self.outFile)
         # Multiple inclusion protection
diff --git a/scripts/layer_dispatch_table_generator.py b/scripts/layer_dispatch_table_generator.py
index 058598d..85b9cd7 100644
--- a/scripts/layer_dispatch_table_generator.py
+++ b/scripts/layer_dispatch_table_generator.py
@@ -93,6 +93,9 @@
     def beginFile(self, genOpts):
         OutputGenerator.beginFile(self, genOpts)
 
+        # Initialize members that require the tree
+        self.handle_types = GetHandleTypes(self.registry.tree)
+
         # User-supplied prefix text, if any (list of strings)
         if (genOpts.prefixText):
             for s in genOpts.prefixText:
@@ -198,8 +201,6 @@
     #
     # Determine if this API should be ignored or added to the instance or device dispatch table
     def AddCommandToDispatchList(self, extension_name, extension_type, name, cmdinfo, handle_type):
-        handle = self.registry.tree.find("types/type/[name='" + handle_type + "'][@category='handle']")
-
         return_type =  cmdinfo.elem.find('proto/type')
         if (return_type is not None and return_type.text == 'void'):
            return_type = None
@@ -223,7 +224,7 @@
             cmd_params.append(self.CommandParam(type=param_type, name=param_name,
                                                 cdecl=param_cdecl))
 
-        if handle is not None and handle_type != 'VkInstance' and handle_type != 'VkPhysicalDevice':
+        if handle_type in self.handle_types and handle_type != 'VkInstance' and handle_type != 'VkPhysicalDevice':
             # The Core Vulkan code will be wrapped in a feature called VK_VERSION_#_#
             # For example: VK_VERSION_1_0 wraps the core 1.0 Vulkan functionality
             if 'VK_VERSION_' in extension_name:
diff --git a/scripts/object_tracker_generator.py b/scripts/object_tracker_generator.py
index 60ffdea..e0a2afa 100644
--- a/scripts/object_tracker_generator.py
+++ b/scripts/object_tracker_generator.py
@@ -346,6 +346,10 @@
     def beginFile(self, genOpts):
         OutputGenerator.beginFile(self, genOpts)
 
+        # Initialize members that require the tree
+        self.handle_types = GetHandleTypes(self.registry.tree)
+        self.type_categories = GetTypeCategories(self.registry.tree)
+
         header_file = (genOpts.filename == 'object_tracker.h')
         source_file = (genOpts.filename == 'object_tracker.cpp')
 
@@ -498,28 +502,13 @@
                 ispointer = True
         return ispointer
     #
-    # Get the category of a type
-    def getTypeCategory(self, typename):
-        types = self.registry.tree.findall("types/type")
-        for elem in types:
-            if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
-                return elem.attrib.get('category')
-    #
     # Check if a parent object is dispatchable or not
     def isHandleTypeObject(self, handletype):
-        handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']")
-        if handle is not None:
-            return True
-        else:
-            return False
+        return handletype in self.handle_types
     #
     # Check if a parent object is dispatchable or not
     def isHandleTypeNonDispatchable(self, handletype):
-        handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']")
-        if handle is not None and handle.find('type').text == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE':
-            return True
-        else:
-            return False
+        return self.handle_types.get(handletype) == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'
     #
     # Retrieve the type and name for a parameter
     def getTypeNameTuple(self, param):
@@ -628,7 +617,7 @@
         struct_list = set()
         for item in item_list:
             paramtype = item.find('type')
-            typecategory = self.getTypeCategory(paramtype.text)
+            typecategory = self.type_categories[paramtype.text]
             if typecategory == 'struct':
                 if self.struct_contains_object(paramtype.text) == True:
                     struct_list.add(item)
diff --git a/scripts/thread_safety_generator.py b/scripts/thread_safety_generator.py
index 91afab2..54cef56 100644
--- a/scripts/thread_safety_generator.py
+++ b/scripts/thread_safety_generator.py
@@ -683,19 +683,11 @@
 
     # Check if an object is a non-dispatchable handle
     def isHandleTypeNonDispatchable(self, handletype):
-        handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']")
-        if handle is not None and handle.find('type').text == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE':
-            return True
-        else:
-            return False
+        return self.handle_types.get(handletype) == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'
 
     # Check if an object is a dispatchable handle
     def isHandleTypeDispatchable(self, handletype):
-        handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']")
-        if handle is not None and handle.find('type').text == 'VK_DEFINE_HANDLE':
-            return True
-        else:
-            return False
+        return self.handle_types.get(handletype) == 'VK_DEFINE_HANDLE'
 
     def makeThreadUseBlock(self, cmd, functionprefix):
         """Generate C function pointer typedef for <command> Element"""
@@ -808,7 +800,10 @@
             return paramdecl
     def beginFile(self, genOpts):
         OutputGenerator.beginFile(self, genOpts)
-        #
+        
+        # Initialize members that require the tree
+        self.handle_types = GetHandleTypes(self.registry.tree)
+
         # TODO: LUGMAL -- remove this and add our copyright
         # User-supplied prefix text, if any (list of strings)
         write(self.inline_copyright_message, file=self.outFile)