loader: Make the ICD library scan happen as needed rather than once

Add a one time loader init routine also since this was done by ICD scan.
Add icd library struct to instance object.
diff --git a/loader/loader.c b/loader/loader.c
index d2c6720..abb834f 100644
--- a/loader/loader.c
+++ b/loader/loader.c
@@ -105,7 +105,7 @@
     .DbgDestroyMsgCallback = loader_DbgDestroyMsgCallback,
 };
 
-LOADER_PLATFORM_THREAD_ONCE_DECLARATION(once_icd);
+LOADER_PLATFORM_THREAD_ONCE_DECLARATION(once_init);
 
 void* loader_heap_alloc(
     struct loader_instance     *instance,
@@ -794,22 +794,22 @@
  * linked directly with the loader.
  */
 
-void loader_get_icd_loader_instance_extensions(struct loader_extension_list *inst_exts)
+void loader_get_icd_loader_instance_extensions(
+                                        struct loader_icd_libs *icd_libs,
+                                        struct loader_extension_list *inst_exts)
 {
-    struct loader_scanned_icds *icd_list = loader.scanned_icd_list;
     struct loader_extension_list icd_exts;
     loader_log(VK_DBG_REPORT_DEBUG_BIT, 0, "Build ICD instance extension list");
     // traverse scanned icd list adding non-duplicate extensions to the list
-    while (icd_list != NULL) {
+    for (uint32_t i = 0; i < icd_libs->count; i++) {
         loader_init_ext_list(&icd_exts);
-        loader_add_global_extensions(icd_list->GetGlobalExtensionProperties,
-                                     icd_list->lib_name,
+        loader_add_global_extensions(icd_libs->list[i].GetGlobalExtensionProperties,
+                                     icd_libs->list[i].lib_name,
                                      &icd_exts);
         loader_add_to_ext_list(inst_exts,
                                icd_exts.count,
                                icd_exts.list);
         loader_destroy_ext_list(&icd_exts);
-        icd_list = icd_list->next;
     };
 
     // Traverse loader's extensions, adding non-duplicate extensions to the list
@@ -931,7 +931,31 @@
     return icd;
 }
 
-static void loader_scanned_icd_add(const char *filename)
+void loader_scanned_icd_clear(struct loader_icd_libs *icd_libs)
+{
+    if (icd_libs->capacity == 0)
+        return;
+    for (uint32_t i = 0; i < icd_libs->count; i++) {
+        loader_platform_close_library(icd_libs->list[i].handle);
+        free(icd_libs->list[i].lib_name);
+    }
+    free(icd_libs->list);
+    icd_libs->capacity = 0;
+    icd_libs->count = 0;
+    icd_libs->list = NULL;
+}
+
+static void loader_scanned_icd_init(struct loader_icd_libs *icd_libs)
+{
+    loader_scanned_icd_clear(icd_libs);
+    icd_libs->capacity = 8 * sizeof(struct loader_scanned_icds);
+    icd_libs->list = malloc(icd_libs->capacity);
+
+}
+
+static void loader_scanned_icd_add(
+                            struct loader_icd_libs *icd_libs,
+                            const char *filename)
 {
     loader_platform_dl_handle handle;
     PFN_vkCreateInstance fp_create_inst;
@@ -939,6 +963,8 @@
     PFN_vkGetInstanceProcAddr fp_get_proc_addr;
     struct loader_scanned_icds *new_node;
 
+    /* TODO implement ref counting of libraries, for now this function leaves
+       libraries open and the scanned_icd_clear closes them */
     // Used to call: dlopen(filename, RTLD_LAZY);
     handle = loader_platform_open_library(filename);
     if (!handle) {
@@ -960,28 +986,26 @@
 
 #undef LOOKUP_LD
 
-    new_node = (struct loader_scanned_icds *) malloc(sizeof(struct loader_scanned_icds)
-                                                     + strlen(filename) + 1);
-    if (!new_node) {
-        loader_log(VK_DBG_REPORT_WARN_BIT, 0, "Out of memory can't add icd");
-        return;
+    // check for enough capacity
+    if ((icd_libs->count * sizeof(struct loader_scanned_icds)) >= icd_libs->capacity) {
+            // double capacity
+            icd_libs->capacity *= 2;
+            icd_libs->list = realloc(icd_libs->list, icd_libs->capacity);
     }
+    new_node = &(icd_libs->list[icd_libs->count]);
 
     new_node->handle = handle;
     new_node->GetInstanceProcAddr = fp_get_proc_addr;
     new_node->CreateInstance = fp_create_inst;
     new_node->GetGlobalExtensionProperties = fp_get_global_ext_props;
-    new_node->next = loader.scanned_icd_list;
 
-    new_node->lib_name = (char *) (new_node + 1);
+    new_node->lib_name = (char *) malloc(strlen(filename) + 1);
     if (!new_node->lib_name) {
         loader_log(VK_DBG_REPORT_WARN_BIT, 0, "Out of memory can't add icd");
         return;
     }
     strcpy(new_node->lib_name, filename);
-
-    loader.scanned_icd_list = new_node;
-
+    icd_libs->count++;
 }
 
 static bool loader_icd_init_entrys(struct loader_icd *icd,
@@ -1067,6 +1091,15 @@
     }
 }
 
+void loader_initialize(void)
+{
+    // initialize a mutex
+    loader_platform_thread_create_mutex(&loader_lock);
+
+    // initialize logging
+    loader_debug_init();
+}
+
 struct loader_manifest_files {
     uint32_t count;
     char **filename_list;
@@ -1598,6 +1631,15 @@
     return;
 }
 
+void loader_init_icd_lib_list()
+{
+
+}
+
+void loader_destroy_icd_lib_list()
+{
+
+}
 /**
  * Try to find the Vulkan ICD driver(s).
  *
@@ -1607,20 +1649,14 @@
  * manifest files it finds the ICD libraries.
  *
  * \returns
- * void
+ * a list of icds that were discovered
  */
-void loader_icd_scan(void)
+void loader_icd_scan(struct loader_icd_libs *icds)
 {
     char *file_str;
     struct loader_manifest_files manifest_files;
 
-
-    // convenient place to initialize a mutex
-    loader_platform_thread_create_mutex(&loader_lock);
-
-    // convenient place to initialize logging
-    loader_debug_init();
-
+    loader_scanned_icd_init(icds);
     // Get a list of manifest files for ICDs
     loader_get_manifest_files("VK_ICD_FILENAMES", false, DEFAULT_VK_DRIVERS_INFO,
                               &manifest_files);
@@ -1654,7 +1690,7 @@
                     char *dir = def_dir;
 
                     // Print out the paths being searched if debugging is enabled
-                    loader_log(VK_DBG_REPORT_DEBUG_BIT, 0, "Searching for ICD drivers named %s at %s\n", icd_file, dir);
+                    loader_log(VK_DBG_REPORT_DEBUG_BIT, 0, "Searching for ICD drivers named %s default dir %s\n", icd_file, dir);
 
                     // strip off extra quotes
                     if (icd_filename[strlen(icd_filename)  - 1] == '"')
@@ -1664,9 +1700,9 @@
 #if defined(__linux__)
                     char full_path[2048];
                     loader_get_fullpath(icd_filename, dir, sizeof(full_path), full_path);
-                    loader_scanned_icd_add(full_path);
+                    loader_scanned_icd_add(icds, full_path);
 #else // WIN32
-                    loader_scanned_icd_add(icd_filename);
+                    loader_scanned_icd_add(icds, icd_filename);
 #endif
                     free(icd_file);
                 }
@@ -2329,7 +2365,6 @@
         VkInstance*                     pInstance)
 {
     struct loader_instance *ptr_instance = *(struct loader_instance **) pInstance;
-    struct loader_scanned_icds *scanned_icds;
     struct loader_icd *icd;
     VkExtensionProperties *prop;
     char **filtered_extension_names = NULL;
@@ -2357,9 +2392,8 @@
     }
     icd_create_info.ppEnabledExtensionNames = (const char * const *) filtered_extension_names;
 
-    scanned_icds = loader.scanned_icd_list;
-    while (scanned_icds) {
-        icd = loader_icd_add(ptr_instance, scanned_icds);
+    for (uint32_t i = 0; i < ptr_instance->icd_libs.count; i++) {
+        icd = loader_icd_add(ptr_instance, &ptr_instance->icd_libs.list[i]);
         if (icd) {
             icd_create_info.extensionCount = 0;
             for (uint32_t i = 0; i < pCreateInfo->extensionCount; i++) {
@@ -2371,12 +2405,12 @@
                 }
             }
 
-            res = scanned_icds->CreateInstance(&icd_create_info,
+            res = ptr_instance->icd_libs.list[i].CreateInstance(&icd_create_info,
                                            &(icd->instance));
             success = loader_icd_init_entrys(
                                 icd,
                                 icd->instance,
-                                scanned_icds->GetInstanceProcAddr);
+                                ptr_instance->icd_libs.list[i].GetInstanceProcAddr);
 
             if (res != VK_SUCCESS || !success)
             {
@@ -2387,7 +2421,6 @@
                         "ICD ignored: failed to CreateInstance and find entrypoints with ICD");
             }
         }
-        scanned_icds = scanned_icds->next;
     }
 
     /*
@@ -2448,7 +2481,7 @@
         icds = next_icd;
     }
 
-
+    loader_scanned_icd_clear(&ptr_instance->icd_libs);
     loader_destroy_ext_list(&ptr_instance->ext_list);
     return VK_SUCCESS;
 }
@@ -2862,6 +2895,7 @@
     struct loader_extension_list *global_ext_list;
     struct loader_layer_list instance_layers;
     struct loader_extension_list icd_extensions;
+    struct loader_icd_libs icd_libs;
     uint32_t copy_size;
 
     if (pCount == NULL) {
@@ -2869,8 +2903,7 @@
     }
 
     memset(&icd_extensions, 0, sizeof(icd_extensions));
-    /* Scan/discover all ICD libraries in a single-threaded manner */
-    loader_platform_thread_once(&once_icd, loader_icd_scan);
+    loader_platform_thread_once(&once_init, loader_initialize);
 
     //TODO do we still need to lock? for loader.global_extensions
     loader_platform_thread_lock_mutex(&loader_lock);
@@ -2886,8 +2919,12 @@
         }
     }
     else {
+        /* Scan/discover all ICD libraries */
+        memset(&icd_libs, 0 , sizeof(struct loader_icd_libs));
+        loader_icd_scan(&icd_libs);
         /* get extensions from all ICD's, merge so no duplicates */
-        loader_get_icd_loader_instance_extensions(&icd_extensions);
+        loader_get_icd_loader_instance_extensions(&icd_libs, &icd_extensions);
+        loader_scanned_icd_clear(&icd_libs);
         global_ext_list = &icd_extensions;
     }
 
@@ -2927,8 +2964,7 @@
 
     struct loader_layer_list instance_layer_list;
 
-    /* Scan/discover all ICD libraries in a single-threaded manner */
-    loader_platform_thread_once(&once_icd, loader_icd_scan);
+    loader_platform_thread_once(&once_init, loader_initialize);
 
     uint32_t copy_size;
 
diff --git a/loader/loader.h b/loader/loader.h
index 66eba22..2b4e491 100644
--- a/loader/loader.h
+++ b/loader/loader.h
@@ -145,14 +145,21 @@
     PFN_vkGetPhysicalDeviceSurfaceSupportWSI GetPhysicalDeviceSurfaceSupportWSI;
 
     /*
-     * Fill in the cache of available global extensions that operate
-     * with this physical device. This cache will be used to satisfy
+     * Fill in the cache of available device extensions from
+     * this physical device. This cache will be used to satisfy
      * calls to GetPhysicalDeviceExtensionProperties
      */
     struct loader_extension_list device_extension_cache[MAX_GPUS_PER_ICD];
     struct loader_icd *next;
 };
 
+/* per ICD library structure */
+struct loader_icd_libs {
+    size_t capacity;
+    uint32_t count;
+    struct loader_scanned_icds *list;
+};
+
 /* per instance structure */
 struct loader_instance {
     VkLayerInstanceDispatchTable *disp; // must be first entry in structure
@@ -162,6 +169,7 @@
     struct loader_icd *icds;
     struct loader_instance *next;
     struct loader_extension_list ext_list;   // icds and loaders extensions
+    struct loader_icd_libs icd_libs;
     /* TODO: Should keep track of application provided allocation functions */
 
     struct loader_msg_callback_map_entry *icd_msg_callback_map;
@@ -178,14 +186,14 @@
 
 struct loader_struct {
     struct loader_instance *instances;
-    struct loader_scanned_icds *scanned_icd_list;
 
     unsigned int loaded_layer_lib_count;
     struct loader_lib_info *loaded_layer_lib_list;
-
+    // TODO add ref counting of ICD libraries
     char *layer_dirs;
 
     // TODO use this struct loader_layer_library_list scanned_layer_libraries;
+    // TODO add list of icd libraries for ref counting them for closure
 };
 
 struct loader_scanned_icds {
@@ -195,8 +203,6 @@
     PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
     PFN_vkCreateInstance CreateInstance;
     PFN_vkGetGlobalExtensionProperties GetGlobalExtensionProperties;
-    struct loader_scanned_icds *next;
-
 };
 
 static inline struct loader_instance *loader_instance(VkInstance instance) {
@@ -230,8 +236,7 @@
 
 /* global variables used across files */
 extern struct loader_struct loader;
-extern LOADER_PLATFORM_THREAD_ONCE_DEFINITION(once_icd);
-extern LOADER_PLATFORM_THREAD_ONCE_DEFINITION(once_exts);
+extern LOADER_PLATFORM_THREAD_ONCE_DEFINITION(once_init);
 extern loader_platform_thread_mutex loader_lock;
 extern const VkLayerInstanceDispatchTable instance_disp;
 
@@ -322,6 +327,7 @@
         VkDevice*                               pDevice);
 
 /* helper function definitions */
+void loader_initialize(void);
 bool has_vk_extension_property_array(
         const VkExtensionProperties *vk_ext_prop,
         const uint32_t count,
@@ -340,11 +346,14 @@
         struct loader_layer_list *list,
         uint32_t prop_list_count,
         const struct loader_layer_properties *props);
-void loader_icd_scan(void);
+void loader_scanned_icd_clear(struct loader_icd_libs *icd_libs);
+void loader_icd_scan(struct loader_icd_libs *icds);
 void loader_layer_scan(
         struct loader_layer_list *instance_layers,
         struct loader_layer_list *device_layers);
-void loader_get_icd_loader_instance_extensions(struct loader_extension_list *inst_exts);
+void loader_get_icd_loader_instance_extensions(
+        struct loader_icd_libs *icd_libs,
+        struct loader_extension_list *inst_exts);
 
 struct loader_icd * loader_get_icd(
         const VkPhysicalDevice gpu,
diff --git a/loader/trampoline.c b/loader/trampoline.c
index 3c779c7..0b59373 100644
--- a/loader/trampoline.c
+++ b/loader/trampoline.c
@@ -42,12 +42,10 @@
         VkInstance* pInstance)
 {
     struct loader_instance *ptr_instance = NULL;
-
     VkResult res = VK_ERROR_INITIALIZATION_FAILED;
     struct loader_layer_list instance_layer_list;
 
-    /* Scan/discover all ICD libraries in a single-threaded manner */
-    loader_platform_thread_once(&once_icd, loader_icd_scan);
+    loader_platform_thread_once(&once_init, loader_initialize);
 
     /* Due to implicit layers might still need to get layer list even if
      * layerCount == 0 and VK_INSTANCE_LAYERS is unset. For now always
@@ -91,10 +89,15 @@
         ptr_instance->alloc_callbacks.pfnFree = pCreateInfo->pAllocCb->pfnFree;
     }
 
+    /* Scan/discover all ICD libraries */
+    memset(&ptr_instance->icd_libs, 0, sizeof(ptr_instance->icd_libs));
+    loader_icd_scan(&ptr_instance->icd_libs);
+
     /* get extensions from all ICD's, merge so no duplicates, then validate */
-    loader_get_icd_loader_instance_extensions(&ptr_instance->ext_list);
+    loader_get_icd_loader_instance_extensions(&ptr_instance->icd_libs, &ptr_instance->ext_list);
     res = loader_validate_instance_extensions(&ptr_instance->ext_list, &instance_layer_list, pCreateInfo);
     if (res != VK_SUCCESS) {
+        loader_scanned_icd_clear(&ptr_instance->icd_libs);
         loader_destroy_ext_list(&ptr_instance->ext_list);
         loader_platform_thread_unlock_mutex(&loader_lock);
         loader_heap_free(ptr_instance, ptr_instance);
@@ -106,6 +109,7 @@
                              sizeof(VkLayerInstanceDispatchTable),
                              VK_SYSTEM_ALLOC_TYPE_INTERNAL);
     if (ptr_instance->disp == NULL) {
+        loader_scanned_icd_clear(&ptr_instance->icd_libs);
         loader_destroy_ext_list(&ptr_instance->ext_list);
         loader_platform_thread_unlock_mutex(&loader_lock);
         loader_heap_free(ptr_instance, ptr_instance);
@@ -118,6 +122,7 @@
     /* activate any layers on instance chain */
     res = loader_enable_instance_layers(ptr_instance, pCreateInfo, &instance_layer_list);
     if (res != VK_SUCCESS) {
+        loader_scanned_icd_clear(&ptr_instance->icd_libs);
         loader_destroy_ext_list(&ptr_instance->ext_list);
         loader.instances = ptr_instance->next;
         loader_platform_thread_unlock_mutex(&loader_lock);