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
 
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index c66ec95..e12409c 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -38,7 +38,6 @@
     VkAllocationCallbacks allocator;
     VkPhysicalDevice_T physical_device;
     uint64_t next_callback_handle;
-    bool debug_report_enabled;
 };
 
 struct VkQueue_T {
@@ -251,13 +250,15 @@
     instance->allocator = *allocator;
     instance->physical_device.dispatch.magic = HWVULKAN_DISPATCH_MAGIC;
     instance->next_callback_handle = 0;
-    instance->debug_report_enabled = false;
 
     for (uint32_t i = 0; i < create_info->enabledExtensionCount; i++) {
         if (strcmp(create_info->ppEnabledExtensionNames[i],
                    VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) {
-            ALOGV("Enabling " VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
-            instance->debug_report_enabled = true;
+            ALOGV("instance extension '%s' requested",
+                  create_info->ppEnabledExtensionNames[i]);
+        } else {
+            ALOGW("unsupported extension '%s' requested",
+                  create_info->ppEnabledExtensionNames[i]);
         }
     }
 
@@ -375,7 +376,7 @@
 // Device
 
 VkResult CreateDevice(VkPhysicalDevice physical_device,
-                      const VkDeviceCreateInfo*,
+                      const VkDeviceCreateInfo* create_info,
                       const VkAllocationCallbacks* allocator,
                       VkDevice* out_device) {
     VkInstance_T* instance = GetInstanceFromPhysicalDevice(physical_device);
@@ -394,6 +395,13 @@
     std::fill(device->next_handle.begin(), device->next_handle.end(),
               UINT64_C(0));
 
+    for (uint32_t i = 0; i < create_info->enabledExtensionCount; i++) {
+        if (strcmp(create_info->ppEnabledExtensionNames[i],
+                   VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) == 0) {
+            ALOGV("Enabling " VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME);
+        }
+    }
+
     *out_device = device;
     return VK_SUCCESS;
 }
diff --git a/vulkan/tools/vkinfo.cpp b/vulkan/tools/vkinfo.cpp
index 71f989e..a2f4e8f 100644
--- a/vulkan/tools/vkinfo.cpp
+++ b/vulkan/tools/vkinfo.cpp
@@ -32,6 +32,7 @@
 struct GpuInfo {
     VkPhysicalDeviceProperties properties;
     VkPhysicalDeviceMemoryProperties memory;
+    VkPhysicalDeviceFeatures features;
     std::vector<VkQueueFamilyProperties> queue_families;
     std::vector<VkExtensionProperties> extensions;
     std::vector<VkLayerProperties> layers;
@@ -115,6 +116,70 @@
         die("vkEnumerateDeviceExtensionProperties (data)", result);
 }
 
+void GatherGpuInfo(VkPhysicalDevice gpu, GpuInfo& info) {
+    VkResult result;
+    uint32_t count;
+
+    vkGetPhysicalDeviceProperties(gpu, &info.properties);
+    vkGetPhysicalDeviceMemoryProperties(gpu, &info.memory);
+    vkGetPhysicalDeviceFeatures(gpu, &info.features);
+
+    vkGetPhysicalDeviceQueueFamilyProperties(gpu, &count, nullptr);
+    info.queue_families.resize(count);
+    vkGetPhysicalDeviceQueueFamilyProperties(gpu, &count,
+                                             info.queue_families.data());
+
+    result = vkEnumerateDeviceLayerProperties(gpu, &count, nullptr);
+    if (result != VK_SUCCESS)
+        die("vkEnumerateDeviceLayerProperties (count)", result);
+    do {
+        info.layers.resize(count);
+        result =
+            vkEnumerateDeviceLayerProperties(gpu, &count, info.layers.data());
+    } while (result == VK_INCOMPLETE);
+    if (result != VK_SUCCESS)
+        die("vkEnumerateDeviceLayerProperties (data)", result);
+    info.layer_extensions.resize(info.layers.size());
+
+    EnumerateDeviceExtensions(gpu, nullptr, &info.extensions);
+    for (size_t i = 0; i < info.layers.size(); i++) {
+        EnumerateDeviceExtensions(gpu, info.layers[i].layerName,
+                                  &info.layer_extensions[i]);
+    }
+
+    const std::array<const char*, 1> kDesiredExtensions = {
+        {VK_KHR_SWAPCHAIN_EXTENSION_NAME},
+    };
+    const char* extensions[kDesiredExtensions.size()];
+    uint32_t num_extensions = 0;
+    for (const auto& desired_ext : kDesiredExtensions) {
+        bool available = HasExtension(info.extensions, desired_ext);
+        for (size_t i = 0; !available && i < info.layer_extensions.size(); i++)
+            available = HasExtension(info.layer_extensions[i], desired_ext);
+        if (available)
+            extensions[num_extensions++] = desired_ext;
+    }
+
+    VkDevice device;
+    const VkDeviceQueueCreateInfo queue_create_info = {
+        .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+        .queueFamilyIndex = 0,
+        .queueCount = 1,
+    };
+    const VkDeviceCreateInfo create_info = {
+        .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+        .queueCreateInfoCount = 1,
+        .pQueueCreateInfos = &queue_create_info,
+        .enabledExtensionCount = num_extensions,
+        .ppEnabledExtensionNames = extensions,
+        .pEnabledFeatures = &info.features,
+    };
+    result = vkCreateDevice(gpu, &create_info, nullptr, &device);
+    if (result != VK_SUCCESS)
+        die("vkCreateDevice", result);
+    vkDestroyDevice(device, nullptr);
+}
+
 void GatherInfo(VulkanInfo* info) {
     VkResult result;
     uint32_t count;
@@ -173,36 +238,8 @@
         die("vkEnumeratePhysicalDevices (data)", result);
 
     info->gpus.resize(num_gpus);
-    for (size_t gpu_idx = 0; gpu_idx < gpus.size(); gpu_idx++) {
-        VkPhysicalDevice gpu = gpus[gpu_idx];
-        GpuInfo& gpu_info = info->gpus.at(gpu_idx);
-
-        vkGetPhysicalDeviceProperties(gpu, &gpu_info.properties);
-        vkGetPhysicalDeviceMemoryProperties(gpu, &gpu_info.memory);
-
-        vkGetPhysicalDeviceQueueFamilyProperties(gpu, &count, nullptr);
-        gpu_info.queue_families.resize(count);
-        vkGetPhysicalDeviceQueueFamilyProperties(
-            gpu, &count, gpu_info.queue_families.data());
-
-        result = vkEnumerateDeviceLayerProperties(gpu, &count, nullptr);
-        if (result != VK_SUCCESS)
-            die("vkEnumerateDeviceLayerProperties (count)", result);
-        do {
-            gpu_info.layers.resize(count);
-            result = vkEnumerateDeviceLayerProperties(gpu, &count,
-                                                      gpu_info.layers.data());
-        } while (result == VK_INCOMPLETE);
-        if (result != VK_SUCCESS)
-            die("vkEnumerateDeviceLayerProperties (data)", result);
-        gpu_info.layer_extensions.resize(gpu_info.layers.size());
-
-        EnumerateDeviceExtensions(gpu, nullptr, &gpu_info.extensions);
-        for (size_t i = 0; i < gpu_info.layers.size(); i++) {
-            EnumerateDeviceExtensions(gpu, gpu_info.layers[i].layerName,
-                                      &gpu_info.layer_extensions[i]);
-        }
-    }
+    for (size_t i = 0; i < gpus.size(); i++)
+        GatherGpuInfo(gpus[i], info->gpus.at(i));
 
     vkDestroyInstance(instance, nullptr);
 }
@@ -335,15 +372,15 @@
             qprops.minImageTransferGranularity.width,
             qprops.minImageTransferGranularity.height,
             qprops.minImageTransferGranularity.depth);
+    }
 
-        if (!info.extensions.empty()) {
-            printf("    Extensions [%zu]:\n", info.extensions.size());
-            PrintExtensions(info.extensions, "    ");
-        }
-        if (!info.layers.empty()) {
-            printf("    Layers [%zu]:\n", info.layers.size());
-            PrintLayers(info.layers, info.layer_extensions, "      ");
-        }
+    if (!info.extensions.empty()) {
+        printf("    Extensions [%zu]:\n", info.extensions.size());
+        PrintExtensions(info.extensions, "      ");
+    }
+    if (!info.layers.empty()) {
+        printf("    Layers [%zu]:\n", info.layers.size());
+        PrintLayers(info.layers, info.layer_extensions, "      ");
     }
 }