vulkan: Driver device extension enumeration and filtering

- Return VK_ERROR_EXTENSION_NOT_PRESENT if a requested device
  extension is not supported by the loader, driver, or any enabled
  device layer.
- Filter out device extensions not supported by the driver when
  creating the driver device.
- Enumerate device extensions supported by the driver or loader.

Change-Id: I538e37bc74cc7f0eb27b1211b9324fb3b8a06e14
(cherry picked from commit 35873021f4f79ded0f584e433076c2675c6aed69)
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index f2fbf31..3e7fbec 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -28,6 +28,12 @@
 
 using namespace vulkan;
 
+// TODO(jessehall): The whole way we deal with extensions is pretty hokey, and
+// not a good long-term solution. Having a hard-coded enum of extensions is
+// bad, of course. Representing sets of extensions (requested, supported, etc.)
+// as a bitset isn't necessarily bad, if the mapping from extension to bit were
+// dynamic. Need to rethink this completely when there's a little more time.
+
 // TODO(jessehall): This file currently builds up global data structures as it
 // loads, and never cleans them up. This means we're doing heap allocations
 // without going through an app-provided allocator, but worse, we'll leak those
@@ -184,7 +190,7 @@
         }
 
         g_instance_layers.push_back(layer);
-        ALOGV("added instance layer '%s'", props.layerName);
+        ALOGV("  added instance layer '%s'", props.layerName);
     }
     for (size_t i = 0; i < num_device_layers; i++) {
         const VkLayerProperties& props = properties[num_instance_layers + i];
@@ -226,7 +232,7 @@
         }
 
         g_device_layers.push_back(layer);
-        ALOGV("added device layer '%s'", props.layerName);
+        ALOGV("  added device layer '%s'", props.layerName);
     }
 
     dlclose(dlhandle);
@@ -396,6 +402,13 @@
                   : nullptr;
 }
 
+bool LayerRef::SupportsExtension(const char* name) const {
+    return std::find_if(layer_->extensions.cbegin(), layer_->extensions.cend(),
+                        [=](const VkExtensionProperties& ext) {
+                            return strcmp(ext.extensionName, name) == 0;
+                        }) != layer_->extensions.cend();
+}
+
 InstanceExtension InstanceExtensionFromName(const char* name) {
     if (strcmp(name, VK_KHR_SURFACE_EXTENSION_NAME) == 0)
         return kKHR_surface;
@@ -406,4 +419,12 @@
     return kInstanceExtensionCount;
 }
 
+DeviceExtension DeviceExtensionFromName(const char* name) {
+    if (strcmp(name, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0)
+        return kKHR_swapchain;
+    if (strcmp(name, VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) == 0)
+        return kANDROID_native_buffer;
+    return kDeviceExtensionCount;
+}
+
 }  // namespace vulkan
diff --git a/vulkan/libvulkan/loader.cpp b/vulkan/libvulkan/loader.cpp
index 985b7e4..5abd3c5 100644
--- a/vulkan/libvulkan/loader.cpp
+++ b/vulkan/libvulkan/loader.cpp
@@ -268,6 +268,7 @@
     const VkAllocationCallbacks* alloc;
     uint32_t num_physical_devices;
     VkPhysicalDevice physical_devices[kMaxPhysicalDevices];
+    DeviceExtensionSet physical_device_driver_extensions[kMaxPhysicalDevices];
 
     Vector<LayerRef> active_layers;
     VkDebugReportCallbackEXT message;
@@ -275,7 +276,6 @@
 
     struct {
         VkInstance instance;
-        InstanceExtensionSet supported_extensions;
         DriverDispatchTable dispatch;
         uint32_t num_physical_devices;
     } drv;  // may eventually be an array
@@ -582,6 +582,9 @@
         DestroyInstance_Bottom(instance.handle, allocator);
         return VK_ERROR_INITIALIZATION_FAILED;
     }
+
+    Vector<VkExtensionProperties> extensions(
+        Vector<VkExtensionProperties>::allocator_type(instance.alloc));
     for (uint32_t i = 0; i < num_physical_devices; i++) {
         hwvulkan_dispatch_t* pdev_dispatch =
             reinterpret_cast<hwvulkan_dispatch_t*>(
@@ -593,10 +596,41 @@
             return VK_ERROR_INITIALIZATION_FAILED;
         }
         pdev_dispatch->vtbl = instance.dispatch_ptr;
+
+        uint32_t count;
+        if ((result = instance.drv.dispatch.EnumerateDeviceExtensionProperties(
+                 instance.physical_devices[i], nullptr, &count, nullptr)) !=
+            VK_SUCCESS) {
+            ALOGW("driver EnumerateDeviceExtensionProperties(%u) failed: %d", i,
+                  result);
+            continue;
+        }
+        extensions.resize(count);
+        if ((result = instance.drv.dispatch.EnumerateDeviceExtensionProperties(
+                 instance.physical_devices[i], nullptr, &count,
+                 extensions.data())) != VK_SUCCESS) {
+            ALOGW("driver EnumerateDeviceExtensionProperties(%u) failed: %d", i,
+                  result);
+            continue;
+        }
+        ALOGV_IF(count > 0, "driver gpu[%u] supports extensions:", i);
+        for (const auto& extension : extensions) {
+            ALOGV("  %s (v%u)", extension.extensionName, extension.specVersion);
+            DeviceExtension id =
+                DeviceExtensionFromName(extension.extensionName);
+            if (id == kDeviceExtensionCount) {
+                ALOGW("driver gpu[%u] extension '%s' unknown to loader", i,
+                      extension.extensionName);
+            } else {
+                instance.physical_device_driver_extensions[i].set(id);
+            }
+        }
+        // Ignore driver attempts to support loader extensions
+        instance.physical_device_driver_extensions[i].reset(kKHR_swapchain);
     }
     instance.drv.num_physical_devices = num_physical_devices;
-
     instance.num_physical_devices = instance.drv.num_physical_devices;
+
     return VK_SUCCESS;
 }
 
@@ -686,7 +720,7 @@
 
 VKAPI_ATTR
 VkResult EnumerateDeviceExtensionProperties_Bottom(
-    VkPhysicalDevice /*pdev*/,
+    VkPhysicalDevice gpu,
     const char* layer_name,
     uint32_t* properties_count,
     VkExtensionProperties* properties) {
@@ -695,7 +729,26 @@
     if (layer_name) {
         GetDeviceLayerExtensions(layer_name, &extensions, &num_extensions);
     } else {
-        // TODO(jessehall)
+        Instance& instance = GetDispatchParent(gpu);
+        size_t gpu_idx = 0;
+        while (instance.physical_devices[gpu_idx] != gpu)
+            gpu_idx++;
+        const DeviceExtensionSet driver_extensions =
+            instance.physical_device_driver_extensions[gpu_idx];
+
+        // We only support VK_KHR_swapchain if the GPU supports
+        // VK_ANDROID_native_buffer
+        VkExtensionProperties* available = static_cast<VkExtensionProperties*>(
+            alloca(kDeviceExtensionCount * sizeof(VkExtensionProperties)));
+        if (driver_extensions[kANDROID_native_buffer]) {
+            available[num_extensions++] = VkExtensionProperties{
+                VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_SWAPCHAIN_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.
+        extensions = available;
     }
 
     if (!properties || *properties_count > num_extensions)
@@ -717,11 +770,11 @@
 }
 
 VKAPI_ATTR
-VkResult CreateDevice_Bottom(VkPhysicalDevice pdev,
+VkResult CreateDevice_Bottom(VkPhysicalDevice gpu,
                              const VkDeviceCreateInfo* create_info,
                              const VkAllocationCallbacks* allocator,
                              VkDevice* device_out) {
-    Instance& instance = GetDispatchParent(pdev);
+    Instance& instance = GetDispatchParent(gpu);
     VkResult result;
 
     if (!allocator) {
@@ -744,7 +797,41 @@
         return result;
     }
 
-    const char* kAndroidNativeBufferExtensionName = "VK_ANDROID_native_buffer";
+    size_t gpu_idx = 0;
+    while (instance.physical_devices[gpu_idx] != gpu)
+        gpu_idx++;
+
+    uint32_t num_driver_extensions = 0;
+    const char** driver_extensions = static_cast<const char**>(
+        alloca(create_info->enabledExtensionCount * sizeof(const char*)));
+    for (uint32_t i = 0; i < create_info->enabledExtensionCount; i++) {
+        const char* name = create_info->ppEnabledExtensionNames[i];
+
+        DeviceExtension id = DeviceExtensionFromName(name);
+        if (id < kDeviceExtensionCount &&
+            (instance.physical_device_driver_extensions[gpu_idx][id] ||
+             id == kKHR_swapchain)) {
+            if (id == kKHR_swapchain)
+                name = VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME;
+            driver_extensions[num_driver_extensions++] = name;
+            continue;
+        }
+
+        bool supported = false;
+        for (const auto& layer : device->active_layers) {
+            if (layer.SupportsExtension(name))
+                supported = true;
+        }
+        if (!supported) {
+            ALOGE(
+                "requested device extension '%s' not supported by driver or "
+                "any active layers",
+                name);
+            DestroyDevice(device);
+            return VK_ERROR_EXTENSION_NOT_PRESENT;
+        }
+    }
+
     VkDeviceCreateInfo driver_create_info = *create_info;
     driver_create_info.enabledLayerCount = 0;
     driver_create_info.ppEnabledLayerNames = nullptr;
@@ -753,12 +840,12 @@
     // supported by the driver here. Also, add the VK_ANDROID_native_buffer
     // extension to the list iff the VK_KHR_swapchain extension was requested,
     // instead of adding it unconditionally like we do now.
-    driver_create_info.enabledExtensionCount = 1;
-    driver_create_info.ppEnabledExtensionNames = &kAndroidNativeBufferExtensionName;
+    driver_create_info.enabledExtensionCount = num_driver_extensions;
+    driver_create_info.ppEnabledExtensionNames = driver_extensions;
 
     VkDevice drv_device;
-    result = instance.drv.dispatch.CreateDevice(pdev, &driver_create_info, allocator,
-                                                &drv_device);
+    result = instance.drv.dispatch.CreateDevice(gpu, &driver_create_info,
+                                                allocator, &drv_device);
     if (result != VK_SUCCESS) {
         DestroyDevice(device);
         return result;
@@ -817,7 +904,7 @@
     // therefore which functions to return procaddrs for.
     PFN_vkCreateDevice create_device = reinterpret_cast<PFN_vkCreateDevice>(
         next_get_proc_addr(drv_device, "vkCreateDevice"));
-    create_device(pdev, create_info, allocator, &drv_device);
+    create_device(gpu, create_info, allocator, &drv_device);
 
     if (!LoadDeviceDispatchTable(static_cast<VkDevice>(base_object),
                                  next_get_proc_addr, device->dispatch)) {
diff --git a/vulkan/libvulkan/loader.h b/vulkan/libvulkan/loader.h
index 375396e..3e2d1c4 100644
--- a/vulkan/libvulkan/loader.h
+++ b/vulkan/libvulkan/loader.h
@@ -31,6 +31,13 @@
 };
 typedef std::bitset<kInstanceExtensionCount> InstanceExtensionSet;
 
+enum DeviceExtension {
+    kKHR_swapchain,
+    kANDROID_native_buffer,
+    kDeviceExtensionCount
+};
+typedef std::bitset<kDeviceExtensionCount> DeviceExtensionSet;
+
 inline const InstanceDispatchTable& GetDispatchTable(VkInstance instance) {
     return **reinterpret_cast<InstanceDispatchTable**>(instance);
 }
@@ -149,6 +156,8 @@
     PFN_vkGetInstanceProcAddr GetGetInstanceProcAddr() const;
     PFN_vkGetDeviceProcAddr GetGetDeviceProcAddr() const;
 
+    bool SupportsExtension(const char* name) const;
+
    private:
     Layer* layer_;
 };
@@ -166,6 +175,7 @@
 LayerRef GetDeviceLayerRef(const char* name);
 
 InstanceExtension InstanceExtensionFromName(const char* name);
+DeviceExtension DeviceExtensionFromName(const char* name);
 
 }  // namespace vulkan