loader: Preload ICD's to speed up common path

The ideal of having the loader be stateless causes serious slowdowns
in application startup due to the need to load then unload ICD's
repeatedly while calling pre vkCreateInstance functions. By pre-
loading the ICD's which are found using loader_icd_scan, the
unecessary reloading of ICD's is avoided.

The preloaded ICD's will be unloaded with vkDestroyInstance. This
allows subsequent vkCreateInstance calls to use the most recent ICD
on the system, preventing issue where a new driver was installed
but an application couldn't use it unless they unloaded the loader
library completely.

Changes to be committed:
	modified:   loader/loader.c
	modified:   loader/loader.h
	modified:   loader/trampoline.c

Change-Id: Id169f94bea15e569b75c3a663b25444cc6c52c40
diff --git a/loader/loader.c b/loader/loader.c
index 4474f5c..15e4faa 100644
--- a/loader/loader.c
+++ b/loader/loader.c
@@ -122,6 +122,13 @@
 loader_platform_thread_mutex loader_lock;
 loader_platform_thread_mutex loader_json_lock;
 
+// A list of ICDs that gets initialized when the loader does its global initialization. This list should never be used by anything
+// other than EnumerateInstanceExtensionProperties(), vkDestroyInstance, and loader_release(). This list does not change
+// functionality, but the fact that the libraries already been loaded causes any call that needs to load ICD libraries to speed up
+// significantly. This can have a huge impact when making repeated calls to vkEnumerateInstanceExtensionProperties and
+// vkCreateInstance.
+static struct loader_icd_tramp_list scanned_icds;
+
 LOADER_PLATFORM_THREAD_ONCE_DECLARATION(once_init);
 
 void *loader_instance_heap_alloc(const struct loader_instance *instance, size_t size, VkSystemAllocationScope alloc_scope) {
@@ -2450,11 +2457,39 @@
 };
 
 void loader_release() {
+    // Guarantee release of the preloaded ICD libraries. This may have already been called in vkDestroyInstance.
+    loader_unload_preloaded_icds();
+
     // release mutexes
     loader_platform_thread_delete_mutex(&loader_lock);
     loader_platform_thread_delete_mutex(&loader_json_lock);
 }
 
+// Preload the ICD libraries that are likely to be needed so we don't repeatedly load/unload them later
+void loader_preload_icds(void) {
+    loader_platform_thread_lock_mutex(&loader_lock);
+
+    // Already preloaded, skip loading again.
+    if (scanned_icds.scanned_list != NULL) {
+        loader_platform_thread_unlock_mutex(&loader_lock);
+        return;
+    }
+
+    memset(&scanned_icds, 0, sizeof(scanned_icds));
+    VkResult result = loader_icd_scan(NULL, &scanned_icds);
+    if (result != VK_SUCCESS) {
+        loader_scanned_icd_clear(NULL, &scanned_icds);
+    }
+    loader_platform_thread_unlock_mutex(&loader_lock);
+}
+
+// Release the ICD libraries that were preloaded
+void loader_unload_preloaded_icds(void) {
+    loader_platform_thread_lock_mutex(&loader_lock);
+    loader_scanned_icd_clear(NULL, &scanned_icds);
+    loader_platform_thread_unlock_mutex(&loader_lock);
+}
+
 // Get next file or dirname given a string list or registry key path
 //
 // \returns
@@ -7310,6 +7345,9 @@
             }
         }
     } else {
+        // Preload ICD libraries so subsequent calls to EnumerateInstanceExtensionProperties don't have to load them
+        loader_preload_icds();
+
         // Scan/discover all ICD libraries
         memset(&icd_tramp_list, 0, sizeof(icd_tramp_list));
         res = loader_icd_scan(NULL, &icd_tramp_list);
diff --git a/loader/loader.h b/loader/loader.h
index 5e94955..0182c74 100644
--- a/loader/loader.h
+++ b/loader/loader.h
@@ -452,6 +452,8 @@
                                              const VkInstanceCreateInfo *pCreateInfo);
 
 void loader_initialize(void);
+void loader_preload_icds(void);
+void loader_unload_preloaded_icds(void);
 bool has_vk_extension_property_array(const VkExtensionProperties *vk_ext_prop, const uint32_t count,
                                      const VkExtensionProperties *ext_array);
 bool has_vk_extension_property(const VkExtensionProperties *vk_ext_prop, const struct loader_extension_list *ext_list);
diff --git a/loader/trampoline.c b/loader/trampoline.c
index b78638e..49f93b7 100644
--- a/loader/trampoline.c
+++ b/loader/trampoline.c
@@ -656,6 +656,11 @@
                                          ptr_instance->tmp_report_callbacks);
         util_FreeDebugReportCreateInfos(pAllocator, ptr_instance->tmp_report_create_infos, ptr_instance->tmp_report_callbacks);
     }
+
+    // Unload preloaded layers, so if vkEnumerateInstanceExtensionProperties or vkCreateInstance is called again, the ICD's are up
+    // to date
+    loader_unload_preloaded_icds();
+
     loader_instance_heap_free(ptr_instance, ptr_instance->disp);
     loader_instance_heap_free(ptr_instance, ptr_instance);
     loader_platform_thread_unlock_mutex(&loader_lock);