libvulkan: Only load driver functions for enabled extensions

Change-Id: I4105291bd42583a10420681b729f03ecd7a91724
(cherry picked from commit 1c77ae2acda376b791932beffcb931c378e8ee36)
diff --git a/vulkan/libvulkan/dispatch.tmpl b/vulkan/libvulkan/dispatch.tmpl
index 2770538..11ec697 100644
--- a/vulkan/libvulkan/dispatch.tmpl
+++ b/vulkan/libvulkan/dispatch.tmpl
@@ -319,19 +319,27 @@

 bool LoadDriverDispatchTable(VkInstance instance,
                              PFN_vkGetInstanceProcAddr get_proc_addr,
+                             const InstanceExtensionSet& extensions,
                              DriverDispatchTable& dispatch) {«
     bool success = true;
     // clang-format off
   {{range $f := AllCommands $}}
     {{if (Macro "IsInstanceDispatched" $f)}}
       {{if not (Macro "IsLoaderFunction" $f)}}
-    dispatch.{{Macro "BaseName" $f}} = §
-        reinterpret_cast<{{Macro "FunctionPtrName" $f}}>(§
-            get_proc_addr(instance, "{{$f.Name}}"));
-    if (UNLIKELY(!dispatch.{{Macro "BaseName" $f}})) {
-        ALOGE("missing driver proc: %s", "{{$f.Name}}");
-        success = false;
+        {{$ext := GetAnnotation $f "extension"}}
+          {{if $ext}}
+    if (extensions[{{Macro "ExtensionConstant" $ext}}]) {
+          {{end}}
+        dispatch.{{Macro "BaseName" $f}} = §
+            reinterpret_cast<{{Macro "FunctionPtrName" $f}}>(§
+                get_proc_addr(instance, "{{$f.Name}}"));
+        if (UNLIKELY(!dispatch.{{Macro "BaseName" $f}})) {
+            ALOGE("missing driver proc: %s", "{{$f.Name}}");
+            success = false;
+        }
+        {{if $ext}}
     }
+        {{end}}
       {{end}}
     {{end}}
   {{end}}
@@ -407,6 +415,20 @@
 
 {{/*
 -------------------------------------------------------------------------------
+  Map an extension name to InstanceExtension or DeviceExtension enum value
+-------------------------------------------------------------------------------
+*/}}
+{{define "ExtensionConstant"}}
+  {{$name := index $.Arguments 0}}
+  {{     if (eq $name "VK_KHR_surface")}}kKHR_surface
+  {{else if (eq $name "VK_KHR_android_surface")}}kKHR_android_surface
+  {{else if (eq $name "VK_EXT_debug_report")}}kEXT_debug_report
+  {{end}}
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
   Emits a function name without the "vk" prefix.
 -------------------------------------------------------------------------------
 */}}
diff --git a/vulkan/libvulkan/dispatch_gen.cpp b/vulkan/libvulkan/dispatch_gen.cpp
index bf9b38f..ebdf0da 100644
--- a/vulkan/libvulkan/dispatch_gen.cpp
+++ b/vulkan/libvulkan/dispatch_gen.cpp
@@ -1198,6 +1198,7 @@
 
 bool LoadDriverDispatchTable(VkInstance instance,
                              PFN_vkGetInstanceProcAddr get_proc_addr,
+                             const InstanceExtensionSet& extensions,
                              DriverDispatchTable& dispatch) {
     bool success = true;
     // clang-format off
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index 2c1b182..f302307 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -265,4 +265,14 @@
                   : nullptr;
 }
 
+InstanceExtension InstanceExtensionFromName(const char* name) {
+    if (strcmp(name, VK_KHR_SURFACE_EXTENSION_NAME) == 0)
+        return kKHR_surface;
+    if (strcmp(name, VK_KHR_ANDROID_SURFACE_EXTENSION_NAME) == 0)
+        return kKHR_android_surface;
+    if (strcmp(name, VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0)
+        return kEXT_debug_report;
+    return kInstanceExtensionCount;
+}
+
 }  // namespace vulkan
diff --git a/vulkan/libvulkan/loader.cpp b/vulkan/libvulkan/loader.cpp
index 48fc923..079352b 100644
--- a/vulkan/libvulkan/loader.cpp
+++ b/vulkan/libvulkan/loader.cpp
@@ -178,6 +178,8 @@
 // Global Data and Initialization
 
 hwvulkan_device_t* g_hwdevice = nullptr;
+InstanceExtensionSet g_driver_instance_extensions;
+
 void LoadVulkanHAL() {
     static const hwvulkan_module_t* module;
     int result =
@@ -195,6 +197,41 @@
         module = nullptr;
         return;
     }
+
+    VkResult vkresult;
+    uint32_t count;
+    if ((vkresult = g_hwdevice->EnumerateInstanceExtensionProperties(
+             nullptr, &count, nullptr)) != VK_SUCCESS) {
+        ALOGE("driver EnumerateInstanceExtensionProperties failed: %d",
+              vkresult);
+        g_hwdevice->common.close(&g_hwdevice->common);
+        g_hwdevice = nullptr;
+        module = nullptr;
+        return;
+    }
+    VkExtensionProperties* extensions = static_cast<VkExtensionProperties*>(
+        alloca(count * sizeof(VkExtensionProperties)));
+    if ((vkresult = g_hwdevice->EnumerateInstanceExtensionProperties(
+             nullptr, &count, extensions)) != VK_SUCCESS) {
+        ALOGE("driver EnumerateInstanceExtensionProperties failed: %d",
+              vkresult);
+        g_hwdevice->common.close(&g_hwdevice->common);
+        g_hwdevice = nullptr;
+        module = nullptr;
+        return;
+    }
+    ALOGV_IF(count > 0, "Driver-supported instance extensions:");
+    for (uint32_t i = 0; i < count; i++) {
+        ALOGV("  %s (v%u)", extensions[i].extensionName,
+              extensions[i].specVersion);
+        InstanceExtension id =
+            InstanceExtensionFromName(extensions[i].extensionName);
+        if (id != kInstanceExtensionCount)
+            g_driver_instance_extensions.set(id);
+    }
+    // Ignore driver attempts to support loader extensions
+    g_driver_instance_extensions.reset(kKHR_surface);
+    g_driver_instance_extensions.reset(kKHR_android_surface);
 }
 
 bool EnsureInitialized() {
@@ -244,6 +281,7 @@
 
     struct {
         VkInstance instance;
+        InstanceExtensionSet supported_extensions;
         DriverDispatchTable dispatch;
         uint32_t num_physical_devices;
     } drv;  // may eventually be an array
@@ -475,13 +513,27 @@
     VkInstanceCreateInfo driver_create_info = *create_info;
     driver_create_info.enabledLayerCount = 0;
     driver_create_info.ppEnabledLayerNames = nullptr;
-    // TODO(jessehall): We currently only enumerate the VK_KHR_surface and
-    // VK_KHR_android_surface extensions, which we don't allow drivers to
-    // support. As soon as we enumerate instance extensions supported by the
-    // driver, we should instead filter the requested extension list here to
-    // only the extensions supported by the driver.
+
+    InstanceExtensionSet enabled_extensions;
     driver_create_info.enabledExtensionCount = 0;
     driver_create_info.ppEnabledExtensionNames = nullptr;
+    size_t max_names = std::min(create_info->enabledExtensionCount,
+                                g_driver_instance_extensions.count());
+    if (max_names > 0) {
+        const char** names =
+            static_cast<const char**>(alloca(max_names * sizeof(char*)));
+        for (uint32_t i = 0; i < create_info->enabledExtensionCount; i++) {
+            InstanceExtension id = InstanceExtensionFromName(
+                create_info->ppEnabledExtensionNames[i]);
+            if (id != kInstanceExtensionCount &&
+                g_driver_instance_extensions[id]) {
+                names[driver_create_info.enabledExtensionCount++] =
+                    create_info->ppEnabledExtensionNames[i];
+                enabled_extensions.set(id);
+            }
+        }
+        driver_create_info.ppEnabledExtensionNames = names;
+    }
 
     result = g_hwdevice->CreateInstance(&driver_create_info, instance.alloc,
                                         &instance.drv.instance);
@@ -492,7 +544,7 @@
 
     if (!LoadDriverDispatchTable(instance.drv.instance,
                                  g_hwdevice->GetInstanceProcAddr,
-                                 instance.drv.dispatch)) {
+                                 enabled_extensions, instance.drv.dispatch)) {
         DestroyInstance_Bottom(instance.handle, allocator);
         return VK_ERROR_INITIALIZATION_FAILED;
     }
@@ -834,19 +886,22 @@
     if (layer_name) {
         GetLayerExtensions(layer_name, &extensions, &num_extensions);
     } else {
-        static const VkExtensionProperties kInstanceExtensions[] = {
-            {VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_SPEC_VERSION},
-            {VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
-             VK_KHR_ANDROID_SURFACE_SPEC_VERSION}};
-        extensions = kInstanceExtensions;
-        num_extensions = sizeof(kInstanceExtensions) / sizeof(kInstanceExtensions[0]);
-
+        VkExtensionProperties* available = static_cast<VkExtensionProperties*>(
+            alloca(kInstanceExtensionCount * sizeof(VkExtensionProperties)));
+        available[num_extensions++] = VkExtensionProperties{
+            VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_SPEC_VERSION};
+        available[num_extensions++] =
+            VkExtensionProperties{VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
+                                  VK_KHR_ANDROID_SURFACE_SPEC_VERSION};
+        if (g_driver_instance_extensions[kEXT_debug_report]) {
+            available[num_extensions++] =
+                VkExtensionProperties{VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
+                                      VK_EXT_DEBUG_REPORT_SPEC_VERSION};
+        }
         // TODO(jessehall): We need to also enumerate extensions supported by
         // implicitly-enabled layers. Currently we don't have that list of
         // layers until instance creation.
-
-        // TODO(jessehall): We need to also enumerate extensions supported by
-        // any driver.
+        extensions = available;
     }
 
     if (!properties || *properties_count > num_extensions)
diff --git a/vulkan/libvulkan/loader.h b/vulkan/libvulkan/loader.h
index 6e32785..b7edb73 100644
--- a/vulkan/libvulkan/loader.h
+++ b/vulkan/libvulkan/loader.h
@@ -17,10 +17,19 @@
 #ifndef LIBVULKAN_LOADER_H
 #define LIBVULKAN_LOADER_H 1
 
+#include <bitset>
 #include "dispatch_gen.h"
 
 namespace vulkan {
 
+enum InstanceExtension {
+    kKHR_surface,
+    kKHR_android_surface,
+    kEXT_debug_report,
+    kInstanceExtensionCount
+};
+typedef std::bitset<kInstanceExtensionCount> InstanceExtensionSet;
+
 inline const InstanceDispatchTable& GetDispatchTable(VkInstance instance) {
     return **reinterpret_cast<InstanceDispatchTable**>(instance);
 }
@@ -62,6 +71,7 @@
                              DeviceDispatchTable& dispatch);
 bool LoadDriverDispatchTable(VkInstance instance,
                              PFN_vkGetInstanceProcAddr get_proc_addr,
+                             const InstanceExtensionSet& extensions,
                              DriverDispatchTable& dispatch);
 
 // -----------------------------------------------------------------------------
@@ -146,6 +156,8 @@
                         uint32_t* count);
 LayerRef GetLayerRef(const char* name);
 
+InstanceExtension InstanceExtensionFromName(const char* name);
+
 }  // namespace vulkan
 
 #endif  // LIBVULKAN_LOADER_H