vulkan: Implement layer and extension enumeration

Change-Id: I485ebbe3e57da396d361f772793e1e89850c334c
(cherry picked from commit 4bee2c3f2fdff04f1eb437f24a7bcf841364d5b3)
diff --git a/vulkan/libvulkan/Android.mk b/vulkan/libvulkan/Android.mk
index 035a96a..4241757 100644
--- a/vulkan/libvulkan/Android.mk
+++ b/vulkan/libvulkan/Android.mk
@@ -19,11 +19,12 @@
 LOCAL_CFLAGS := -std=c99 -fvisibility=hidden -fstrict-aliasing
 LOCAL_CFLAGS += -DLOG_TAG=\"vulkan\"
 LOCAL_CFLAGS += -Weverything -Werror -Wno-padded -Wno-undef
-LOCAL_CPPFLAGS := -std=c++1y \
+LOCAL_CPPFLAGS := -std=c++14 \
 	-Wno-c++98-compat-pedantic \
 	-Wno-exit-time-destructors \
 	-Wno-c99-extensions \
-	-Wno-zero-length-array
+	-Wno-zero-length-array \
+	-Wno-global-constructors
 
 LOCAL_C_INCLUDES := \
 	frameworks/native/vulkan/include \
@@ -31,6 +32,7 @@
 
 LOCAL_SRC_FILES := \
 	dispatch_gen.cpp \
+	layers_extensions.cpp \
 	loader.cpp \
 	swapchain.cpp \
 	vulkan_loader_data.cpp
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
new file mode 100644
index 0000000..e56cdea
--- /dev/null
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+
+#include "loader.h"
+#include <alloca.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <mutex>
+#include <sys/prctl.h>
+#include <string>
+#include <string.h>
+#include <vector>
+#include <log/log.h>
+#include <vulkan/vulkan_loader_data.h>
+
+using namespace vulkan;
+
+namespace vulkan {
+struct Layer {
+    VkLayerProperties properties;
+    size_t library_idx;
+    std::vector<VkExtensionProperties> extensions;
+};
+}  // namespace vulkan
+
+namespace {
+
+std::mutex g_library_mutex;
+struct LayerLibrary {
+    std::string path;
+    void* dlhandle;
+    size_t refcount;
+};
+std::vector<LayerLibrary> g_layer_libraries;
+std::vector<Layer> g_layers;
+
+void AddLayerLibrary(const std::string& path) {
+    ALOGV("examining layer library '%s'", path.c_str());
+
+    void* dlhandle = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
+    if (!dlhandle) {
+        ALOGW("failed to load layer library '%s': %s", path.c_str(), dlerror());
+        return;
+    }
+
+    PFN_vkEnumerateInstanceLayerProperties enumerate_layer_properties =
+        reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(
+            dlsym(dlhandle, "vkEnumerateInstanceLayerProperties"));
+    if (!enumerate_layer_properties) {
+        ALOGW(
+            "failed to find vkEnumerateInstanceLayerProperties in library "
+            "'%s': %s",
+            path.c_str(), dlerror());
+        dlclose(dlhandle);
+        return;
+    }
+    PFN_vkEnumerateInstanceExtensionProperties enumerate_extension_properties =
+        reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties>(
+            dlsym(dlhandle, "vkEnumerateInstanceExtensionProperties"));
+    if (!enumerate_extension_properties) {
+        ALOGW(
+            "failed to find vkEnumerateInstanceExtensionProperties in library "
+            "'%s': %s",
+            path.c_str(), dlerror());
+        dlclose(dlhandle);
+        return;
+    }
+
+    uint32_t layer_count;
+    VkResult result = enumerate_layer_properties(&layer_count, nullptr);
+    if (result != VK_SUCCESS) {
+        ALOGW("vkEnumerateInstanceLayerProperties failed for library '%s': %d",
+              path.c_str(), result);
+        dlclose(dlhandle);
+        return;
+    }
+    VkLayerProperties* properties = static_cast<VkLayerProperties*>(
+        alloca(layer_count * sizeof(VkLayerProperties)));
+    result = enumerate_layer_properties(&layer_count, properties);
+    if (result != VK_SUCCESS) {
+        ALOGW("vkEnumerateInstanceLayerProperties failed for library '%s': %d",
+              path.c_str(), result);
+        dlclose(dlhandle);
+        return;
+    }
+
+    size_t library_idx = g_layer_libraries.size();
+    g_layers.reserve(g_layers.size() + layer_count);
+    for (size_t i = 0; i < layer_count; i++) {
+        Layer layer;
+        layer.properties = properties[i];
+        layer.library_idx = library_idx;
+
+        uint32_t count;
+        result = enumerate_extension_properties(properties[i].layerName, &count,
+                                                nullptr);
+        if (result != VK_SUCCESS) {
+            ALOGW(
+                "vkEnumerateInstanceExtensionProperties(%s) failed for library "
+                "'%s': %d",
+                properties[i].layerName, path.c_str(), result);
+            g_layers.resize(g_layers.size() - (i + 1));
+            dlclose(dlhandle);
+            return;
+        }
+        layer.extensions.resize(count);
+        result = enumerate_extension_properties(properties[i].layerName, &count,
+                                                layer.extensions.data());
+        if (result != VK_SUCCESS) {
+            ALOGW(
+                "vkEnumerateInstanceExtensionProperties(%s) failed for library "
+                "'%s': %d",
+                properties[i].layerName, path.c_str(), result);
+            g_layers.resize(g_layers.size() - (i + 1));
+            dlclose(dlhandle);
+            return;
+        }
+
+        g_layers.push_back(layer);
+        ALOGV("found layer '%s'", properties[i].layerName);
+    }
+
+    dlclose(dlhandle);
+
+    g_layer_libraries.push_back(LayerLibrary{path, nullptr, 0});
+}
+
+void DiscoverLayersInDirectory(const std::string& dir_path) {
+    ALOGV("looking for layers in '%s'", dir_path.c_str());
+
+    DIR* directory = opendir(dir_path.c_str());
+    if (!directory) {
+        int err = errno;
+        ALOGV_IF(err != ENOENT, "failed to open layer directory '%s': %s (%d)",
+                 dir_path.c_str(), strerror(err), err);
+        return;
+    }
+
+    std::string path;
+    path.reserve(dir_path.size() + 20);
+    path.append(dir_path);
+    path.append("/");
+
+    struct dirent* entry;
+    while ((entry = readdir(directory))) {
+        size_t libname_len = strlen(entry->d_name);
+        if (strncmp(entry->d_name, "libVKLayer", 10) != 0 ||
+            strncmp(entry->d_name + libname_len - 3, ".so", 3) != 0)
+            continue;
+        path.append(entry->d_name);
+        AddLayerLibrary(path);
+        path.resize(dir_path.size() + 1);
+    }
+
+    closedir(directory);
+}
+
+void* GetLayerGetProcAddr(const Layer& layer,
+                          const char* gpa_name,
+                          size_t gpa_name_len) {
+    const LayerLibrary& library = g_layer_libraries[layer.library_idx];
+    void* gpa;
+    size_t layer_name_len = std::max(2u, strlen(layer.properties.layerName));
+    char* name = static_cast<char*>(alloca(layer_name_len + gpa_name_len + 1));
+    strcpy(name, layer.properties.layerName);
+    strcpy(name + layer_name_len, gpa_name);
+    if (!(gpa = dlsym(library.dlhandle, name))) {
+        strcpy(name, "vk");
+        strcpy(name + 2, gpa_name);
+        gpa = dlsym(library.dlhandle, name);
+    }
+    return gpa;
+}
+
+}  // anonymous namespace
+
+namespace vulkan {
+
+void DiscoverLayers() {
+    if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0))
+        DiscoverLayersInDirectory("/data/local/debug/vulkan");
+    if (!LoaderData::GetInstance().layer_path.empty())
+        DiscoverLayersInDirectory(LoaderData::GetInstance().layer_path.c_str());
+}
+
+uint32_t EnumerateLayers(uint32_t count, VkLayerProperties* properties) {
+    uint32_t n = std::min(count, g_layers.size());
+    for (uint32_t i = 0; i < n; i++) {
+        properties[i] = g_layers[i].properties;
+    }
+    return g_layers.size();
+}
+
+void GetLayerExtensions(const char* name,
+                        const VkExtensionProperties** properties,
+                        uint32_t* count) {
+    for (const auto& layer : g_layers) {
+        if (strcmp(name, layer.properties.layerName) != 0)
+            continue;
+        *properties = layer.extensions.data();
+        *count = layer.extensions.size();
+    }
+}
+
+LayerRef GetLayerRef(const char* name) {
+    for (uint32_t id = 0; id < g_layers.size(); id++) {
+        if (strcmp(name, g_layers[id].properties.layerName) != 0) {
+            LayerLibrary& library = g_layer_libraries[g_layers[id].library_idx];
+            std::lock_guard<std::mutex> lock(g_library_mutex);
+            if (library.refcount++ == 0) {
+                library.dlhandle =
+                    dlopen(library.path.c_str(), RTLD_NOW | RTLD_LOCAL);
+                if (!library.dlhandle) {
+                    ALOGE("failed to load layer library '%s': %s",
+                          library.path.c_str(), dlerror());
+                    library.refcount = 0;
+                    return LayerRef(nullptr);
+                }
+            }
+            return LayerRef(&g_layers[id]);
+        }
+    }
+    return LayerRef(nullptr);
+}
+
+LayerRef::LayerRef(Layer* layer) : layer_(layer) {}
+
+LayerRef::~LayerRef() {
+    if (layer_) {
+        LayerLibrary& library = g_layer_libraries[layer_->library_idx];
+        std::lock_guard<std::mutex> lock(g_library_mutex);
+        if (--library.refcount == 0) {
+            dlclose(library.dlhandle);
+            library.dlhandle = nullptr;
+        }
+    }
+}
+
+LayerRef::LayerRef(LayerRef&& other) : layer_(std::move(other.layer_)) {}
+
+PFN_vkGetInstanceProcAddr LayerRef::GetGetInstanceProcAddr() const {
+    return layer_ ? reinterpret_cast<PFN_vkGetInstanceProcAddr>(
+                        GetLayerGetProcAddr(*layer_, "GetInstanceProcAddr", 19))
+                  : nullptr;
+}
+
+PFN_vkGetDeviceProcAddr LayerRef::GetGetDeviceProcAddr() const {
+    return layer_ ? reinterpret_cast<PFN_vkGetDeviceProcAddr>(
+                        GetLayerGetProcAddr(*layer_, "GetDeviceProcAddr", 17))
+                  : nullptr;
+}
+
+}  // namespace vulkan
diff --git a/vulkan/libvulkan/loader.cpp b/vulkan/libvulkan/loader.cpp
index f884e8b..a57e5da 100644
--- a/vulkan/libvulkan/loader.cpp
+++ b/vulkan/libvulkan/loader.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-//#define LOG_NDEBUG 0
+#define LOG_NDEBUG 0
 
 // module header
 #include "loader.h"
@@ -46,9 +46,6 @@
 
 namespace {
 
-// Define Handle typedef to be void* as returned from dlopen.
-typedef void* SharedLibraryHandle;
-
 // These definitions are taken from the LunarG Vulkan Loader. They are used to
 // enforce compatability between the Loader and Layers.
 typedef void* (*PFN_vkGetProcAddr)(void* obj, const char* pName);
@@ -124,16 +121,6 @@
 
 // ----------------------------------------------------------------------------
 
-struct LayerData {
-    String path;
-    SharedLibraryHandle handle;
-    uint32_t ref_count;
-};
-
-typedef UnorderedMap<String, LayerData>::iterator LayerMapIterator;
-
-// ----------------------------------------------------------------------------
-
 VKAPI_ATTR void* DefaultAllocate(void*,
                                  size_t size,
                                  size_t alignment,
@@ -188,33 +175,35 @@
 };
 
 // ----------------------------------------------------------------------------
+// Global Data and Initialization
 
-hwvulkan_device_t* g_hwdevice;
+hwvulkan_device_t* g_hwdevice = nullptr;
+void LoadVulkanHAL() {
+    static const hwvulkan_module_t* module;
+    int result =
+        hw_get_module("vulkan", reinterpret_cast<const hw_module_t**>(&module));
+    if (result != 0) {
+        ALOGE("failed to load vulkan hal: %s (%d)", strerror(-result), result);
+        return;
+    }
+    result = module->common.methods->open(
+        &module->common, HWVULKAN_DEVICE_0,
+        reinterpret_cast<hw_device_t**>(&g_hwdevice));
+    if (result != 0) {
+        ALOGE("failed to open vulkan driver: %s (%d)", strerror(-result),
+              result);
+        module = nullptr;
+        return;
+    }
+}
+
 bool EnsureInitialized() {
     static std::once_flag once_flag;
-    static const hwvulkan_module_t* module;
-
     std::call_once(once_flag, []() {
-        int result;
-        result = hw_get_module("vulkan",
-                               reinterpret_cast<const hw_module_t**>(&module));
-        if (result != 0) {
-            ALOGE("failed to load vulkan hal: %s (%d)", strerror(-result),
-                  result);
-            return;
-        }
-        result = module->common.methods->open(
-            &module->common, HWVULKAN_DEVICE_0,
-            reinterpret_cast<hw_device_t**>(&g_hwdevice));
-        if (result != 0) {
-            ALOGE("failed to open vulkan driver: %s (%d)", strerror(-result),
-                  result);
-            module = nullptr;
-            return;
-        }
+        LoadVulkanHAL();
+        DiscoverLayers();
     });
-
-    return module != nullptr && g_hwdevice != nullptr;
+    return g_hwdevice != nullptr;
 }
 
 // -----------------------------------------------------------------------------
@@ -226,18 +215,16 @@
           get_instance_proc_addr(nullptr),
           alloc(alloc_callbacks),
           num_physical_devices(0),
-          layers(CallbackAllocator<std::pair<String, LayerData>>(alloc)),
-          active_layers(CallbackAllocator<String>(alloc)),
+          active_layers(CallbackAllocator<LayerRef>(alloc)),
           message(VK_NULL_HANDLE) {
         memset(&dispatch, 0, sizeof(dispatch));
         memset(physical_devices, 0, sizeof(physical_devices));
-        pthread_mutex_init(&layer_lock, 0);
         drv.instance = VK_NULL_HANDLE;
         memset(&drv.dispatch, 0, sizeof(drv.dispatch));
         drv.num_physical_devices = 0;
     }
 
-    ~Instance() { pthread_mutex_destroy(&layer_lock); }
+    ~Instance() {}
 
     const InstanceDispatchTable* dispatch_ptr;
     const VkInstance handle;
@@ -252,11 +239,7 @@
     uint32_t num_physical_devices;
     VkPhysicalDevice physical_devices[kMaxPhysicalDevices];
 
-    pthread_mutex_t layer_lock;
-    // Map of layer names to layer data
-    UnorderedMap<String, LayerData> layers;
-    // Vector of layers active for this instance
-    Vector<LayerMapIterator> active_layers;
+    Vector<LayerRef> active_layers;
     VkDbgMsgCallback message;
 
     struct {
@@ -269,14 +252,13 @@
 struct Device {
     Device(Instance* instance_)
         : instance(instance_),
-          active_layers(CallbackAllocator<LayerMapIterator>(instance->alloc)) {
+          active_layers(CallbackAllocator<LayerRef>(instance->alloc)) {
         memset(&dispatch, 0, sizeof(dispatch));
     }
     DeviceDispatchTable dispatch;
     Instance* instance;
     PFN_vkGetDeviceProcAddr get_device_proc_addr;
-    // Vector of layers active for this device
-    Vector<LayerMapIterator> active_layers;
+    Vector<LayerRef> active_layers;
 };
 
 template <typename THandle>
@@ -329,106 +311,16 @@
     alloc->pfnFree(alloc->pUserData, device);
 }
 
-void FindLayersInDirectory(Instance& instance, const String& dir_name) {
-    DIR* directory = opendir(dir_name.c_str());
-    if (!directory) {
-        int err = errno;
-        ALOGW_IF(err != ENOENT, "failed to open layer directory '%s': %s (%d)",
-                 dir_name.c_str(), strerror(err), err);
-        return;
-    }
-
-    Vector<VkLayerProperties> properties(
-        CallbackAllocator<VkLayerProperties>(instance.alloc));
-    struct dirent* entry;
-    while ((entry = readdir(directory))) {
-        size_t length = strlen(entry->d_name);
-        if (strncmp(entry->d_name, "libVKLayer", 10) != 0 ||
-            strncmp(entry->d_name + length - 3, ".so", 3) != 0)
-            continue;
-        // Open so
-        SharedLibraryHandle layer_handle =
-            dlopen((dir_name + entry->d_name).c_str(), RTLD_NOW | RTLD_LOCAL);
-        if (!layer_handle) {
-            ALOGE("%s failed to load with error %s; Skipping", entry->d_name,
-                  dlerror());
-            continue;
-        }
-
-        // Get Layers in so
-        PFN_vkEnumerateInstanceLayerProperties get_layer_properties =
-            reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(
-                dlsym(layer_handle, "vkEnumerateInstanceLayerProperties"));
-        if (!get_layer_properties) {
-            ALOGE(
-                "%s failed to find vkEnumerateInstanceLayerProperties with "
-                "error %s; Skipping",
-                entry->d_name, dlerror());
-            dlclose(layer_handle);
-            continue;
-        }
-        uint32_t count;
-        get_layer_properties(&count, nullptr);
-
-        properties.resize(count);
-        get_layer_properties(&count, &properties[0]);
-
-        // Add Layers to potential list
-        for (uint32_t i = 0; i < count; ++i) {
-            String layer_name(properties[i].layerName,
-                              CallbackAllocator<char>(instance.alloc));
-            LayerData layer_data = {dir_name + entry->d_name, 0, 0};
-            instance.layers.insert(std::make_pair(layer_name, layer_data));
-            ALOGV("Found layer %s", properties[i].layerName);
-        }
-        dlclose(layer_handle);
-    }
-
-    closedir(directory);
-}
-
 template <class TObject>
-void ActivateLayer(TObject* object, Instance* instance, const String& name) {
-    // If object has layer, do nothing
-    auto element = instance->layers.find(name);
-    if (element == instance->layers.end()) {
-        return;
-    }
+bool ActivateLayer(TObject* object, const char* name) {
+    LayerRef layer(GetLayerRef(name));
+    if (!layer)
+        return false;
     if (std::find(object->active_layers.begin(), object->active_layers.end(),
-                  element) != object->active_layers.end()) {
-        ALOGW("Layer %s already activated; skipping", name.c_str());
-        return;
-    }
-    // If layer is not open, open it
-    LayerData& layer_data = element->second;
-    pthread_mutex_lock(&instance->layer_lock);
-    if (layer_data.ref_count == 0) {
-        SharedLibraryHandle layer_handle =
-            dlopen(layer_data.path.c_str(), RTLD_NOW | RTLD_LOCAL);
-        if (!layer_handle) {
-            pthread_mutex_unlock(&instance->layer_lock);
-            ALOGE("%s failed to load with error %s; Skipping",
-                  layer_data.path.c_str(), dlerror());
-            return;
-        }
-        layer_data.handle = layer_handle;
-    }
-    layer_data.ref_count++;
-    pthread_mutex_unlock(&instance->layer_lock);
-    ALOGV("Activating layer %s", name.c_str());
-    object->active_layers.push_back(element);
-}
-
-void DeactivateLayer(Instance& instance,
-                     Vector<LayerMapIterator>::iterator& element) {
-    LayerMapIterator& layer_map_data = *element;
-    LayerData& layer_data = layer_map_data->second;
-    pthread_mutex_lock(&instance.layer_lock);
-    layer_data.ref_count--;
-    if (!layer_data.ref_count) {
-        dlclose(layer_data.handle);
-    }
-    pthread_mutex_unlock(&instance.layer_lock);
+                  layer) == object->active_layers.end())
+        object->active_layers.push_back(std::move(layer));
+    ALOGV("activated layer '%s'", name);
+    return true;
 }
 
 struct InstanceNamesPair {
@@ -478,7 +370,7 @@
         size_t end, start = 0;
         while ((end = layer_prop_str.find(':', start)) != std::string::npos) {
             layer_name = layer_prop_str.substr(start, end - start);
-            ActivateLayer(object, instance, layer_name);
+            ActivateLayer(object, layer_name.c_str());
             start = end + 1;
         }
         Vector<String> layer_names(CallbackAllocator<String>(instance->alloc));
@@ -487,23 +379,18 @@
         property_list(SetLayerNamesFromProperty,
                       static_cast<void*>(&instance_names_pair));
         for (auto layer_name_element : layer_names) {
-            ActivateLayer(object, instance, layer_name_element);
+            ActivateLayer(object, layer_name_element.c_str());
         }
     }
     // Load app layers
     for (uint32_t i = 0; i < create_info->enabledLayerNameCount; ++i) {
-        String layer_name(create_info->ppEnabledLayerNames[i],
-                          string_allocator);
-        auto element = instance->layers.find(layer_name);
-        if (element == instance->layers.end()) {
+        if (!ActivateLayer(object, create_info->ppEnabledLayerNames[i])) {
             ALOGE("requested %s layer '%s' not present",
                   create_info->sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
                       ? "instance"
                       : "device",
-                  layer_name.c_str());
+                  create_info->ppEnabledLayerNames[i]);
             return VK_ERROR_LAYER_NOT_PRESENT;
-        } else {
-            ActivateLayer(object, instance, layer_name);
         }
     }
     return VK_SUCCESS;
@@ -736,22 +623,22 @@
 
 VKAPI_ATTR
 VkResult EnumerateDeviceExtensionProperties_Bottom(
-    VkPhysicalDevice pdev,
-    const char* layer_name,
+    VkPhysicalDevice /*pdev*/,
+    const char* /*layer_name*/,
     uint32_t* properties_count,
-    VkExtensionProperties* properties) {
-    // TODO: what are we supposed to do with layer_name here?
-    return GetDispatchParent(pdev)
-        .drv.dispatch.EnumerateDeviceExtensionProperties(
-            pdev, layer_name, properties_count, properties);
+    VkExtensionProperties* /*properties*/) {
+    // TODO(jessehall): Implement me...
+    *properties_count = 0;
+    return VK_SUCCESS;
 }
 
 VKAPI_ATTR
-VkResult EnumerateDeviceLayerProperties_Bottom(VkPhysicalDevice pdev,
+VkResult EnumerateDeviceLayerProperties_Bottom(VkPhysicalDevice /*pdev*/,
                                                uint32_t* properties_count,
-                                               VkLayerProperties* properties) {
-    return GetDispatchParent(pdev).drv.dispatch.EnumerateDeviceLayerProperties(
-        pdev, properties_count, properties);
+                                               VkLayerProperties* /*properties*/) {
+    // TODO(jessehall): Implement me...
+    *properties_count = 0;
+    return VK_SUCCESS;
 }
 
 VKAPI_ATTR
@@ -825,20 +712,11 @@
         next_element->next_element = next_object;
         next_object = static_cast<void*>(next_element);
 
-        auto& name = device->active_layers[idx]->first;
-        auto& handle = device->active_layers[idx]->second.handle;
-        next_get_proc_addr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(
-            dlsym(handle, (name + "GetDeviceProcAddr").c_str()));
+        next_get_proc_addr = device->active_layers[idx].GetGetDeviceProcAddr();
         if (!next_get_proc_addr) {
+            next_object = next_element->next_element;
             next_get_proc_addr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(
-                dlsym(handle, "vkGetDeviceProcAddr"));
-            if (!next_get_proc_addr) {
-                ALOGE("Cannot find vkGetDeviceProcAddr for %s, error is %s",
-                      name.c_str(), dlerror());
-                next_object = next_element->next_element;
-                next_get_proc_addr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(
-                    next_element->get_proc_addr);
-            }
+                next_element->get_proc_addr);
         }
     }
 
@@ -882,10 +760,7 @@
                 vkGetInstanceProcAddr(vkinstance, "vkDbgDestroyMsgCallback"));
         DebugDestroyMessageCallback(vkinstance, instance.message);
     }
-    for (auto it = instance.active_layers.begin();
-         it != instance.active_layers.end(); ++it) {
-        DeactivateLayer(instance, it);
-    }
+    instance.active_layers.clear();
     const VkAllocationCallbacks* alloc = instance.alloc;
     instance.~Instance();
     alloc->pfnFree(alloc->pUserData, &instance);
@@ -925,30 +800,44 @@
 // through a dispatch table.
 
 VkResult EnumerateInstanceExtensionProperties_Top(
-    const char* /*layer_name*/,
-    uint32_t* count,
-    VkExtensionProperties* /*properties*/) {
+    const char* layer_name,
+    uint32_t* properties_count,
+    VkExtensionProperties* properties) {
     if (!EnsureInitialized())
         return VK_ERROR_INITIALIZATION_FAILED;
 
-    // TODO: not yet implemented
-    ALOGW("vkEnumerateInstanceExtensionProperties not implemented");
+    const VkExtensionProperties* extensions = nullptr;
+    uint32_t num_extensions = 0;
+    if (layer_name) {
+        GetLayerExtensions(layer_name, &extensions, &num_extensions);
+    } else {
+        static const VkExtensionProperties kInstanceExtensions[] =
+            {{VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_REVISION},
+             {VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, VK_KHR_ANDROID_SURFACE_REVISION}};
+        extensions = kInstanceExtensions;
+        num_extensions = sizeof(kInstanceExtensions) / sizeof(kInstanceExtensions[0]);
+        // 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.
+    }
 
-    *count = 0;
-    return VK_SUCCESS;
+    if (!properties || *properties_count > num_extensions)
+        *properties_count = num_extensions;
+    if (properties)
+        std::copy(extensions, extensions + *properties_count, properties);
+    return *properties_count < num_extensions ? VK_INCOMPLETE : VK_SUCCESS;
 }
 
-VkResult EnumerateInstanceLayerProperties_Top(
-    uint32_t* count,
-    VkLayerProperties* /*properties*/) {
+VkResult EnumerateInstanceLayerProperties_Top(uint32_t* properties_count,
+                                              VkLayerProperties* properties) {
     if (!EnsureInitialized())
         return VK_ERROR_INITIALIZATION_FAILED;
 
-    // TODO: not yet implemented
-    ALOGW("vkEnumerateInstanceLayerProperties not implemented");
-
-    *count = 0;
-    return VK_SUCCESS;
+    uint32_t layer_count =
+        EnumerateLayers(properties ? *properties_count : 0, properties);
+    if (!properties || *properties_count > layer_count)
+        *properties_count = layer_count;
+    return *properties_count < layer_count ? VK_INCOMPLETE : VK_SUCCESS;
 }
 
 VkResult CreateInstance_Top(const VkInstanceCreateInfo* create_info,
@@ -972,16 +861,6 @@
         return VK_ERROR_OUT_OF_HOST_MEMORY;
     Instance* instance = new (instance_mem) Instance(allocator);
 
-    // Scan layers
-    CallbackAllocator<char> string_allocator(instance->alloc);
-    String dir_name("/data/local/debug/vulkan/", string_allocator);
-    if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0))
-        FindLayersInDirectory(*instance, dir_name);
-    const std::string& path = LoaderData::GetInstance().layer_path;
-    dir_name.assign(path.c_str(), path.size());
-    dir_name.append("/");
-    FindLayersInDirectory(*instance, dir_name);
-
     result = ActivateAllLayers(create_info, instance, instance);
     if (result != VK_SUCCESS) {
         DestroyInstance_Bottom(instance->handle, allocator);
@@ -1005,21 +884,12 @@
         next_element->next_element = next_object;
         next_object = static_cast<void*>(next_element);
 
-        auto& name = instance->active_layers[idx]->first;
-        auto& handle = instance->active_layers[idx]->second.handle;
-        next_get_proc_addr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(
-            dlsym(handle, (name + "GetInstanceProcAddr").c_str()));
+        next_get_proc_addr =
+            instance->active_layers[idx].GetGetInstanceProcAddr();
         if (!next_get_proc_addr) {
+            next_object = next_element->next_element;
             next_get_proc_addr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(
-                dlsym(handle, "vkGetInstanceProcAddr"));
-            if (!next_get_proc_addr) {
-                ALOGE("Cannot find vkGetInstanceProcAddr for %s, error is %s",
-                      name.c_str(), dlerror());
-                next_object = next_element->next_element;
-                next_get_proc_addr =
-                    reinterpret_cast<PFN_vkGetInstanceProcAddr>(
-                        next_element->get_proc_addr);
-            }
+                next_element->get_proc_addr);
         }
     }
     instance->get_instance_proc_addr = next_get_proc_addr;
@@ -1167,12 +1037,6 @@
     if (!vkdevice)
         return;
     Device& device = GetDispatchParent(vkdevice);
-    // TODO(jessehall): This seems very wrong. We might close a layer library
-    // right before we call DestroyDevice in it.
-    for (auto it = device.active_layers.begin();
-         it != device.active_layers.end(); ++it) {
-        DeactivateLayer(*device.instance, it);
-    }
     device.dispatch.DestroyDevice(vkdevice, device.instance->alloc);
     DestroyDevice(&device);
 }
diff --git a/vulkan/libvulkan/loader.h b/vulkan/libvulkan/loader.h
index e3a7f0f..1511c3a 100644
--- a/vulkan/libvulkan/loader.h
+++ b/vulkan/libvulkan/loader.h
@@ -117,6 +117,35 @@
 VKAPI_ATTR VkResult QueuePresentKHR_Bottom(VkQueue queue, const VkPresentInfoKHR* present_info);
 // clang-format on
 
+// -----------------------------------------------------------------------------
+// layers_extensions.cpp
+
+struct Layer;
+class LayerRef {
+   public:
+    LayerRef(Layer* layer);
+    LayerRef(LayerRef&& other);
+    ~LayerRef();
+    LayerRef(const LayerRef&) = delete;
+    LayerRef& operator=(const LayerRef&) = delete;
+
+    // provides bool-like behavior
+    operator const Layer*() const { return layer_; }
+
+    PFN_vkGetInstanceProcAddr GetGetInstanceProcAddr() const;
+    PFN_vkGetDeviceProcAddr GetGetDeviceProcAddr() const;
+
+   private:
+    Layer* layer_;
+};
+
+void DiscoverLayers();
+uint32_t EnumerateLayers(uint32_t count, VkLayerProperties* properties);
+void GetLayerExtensions(const char* name,
+                        const VkExtensionProperties** properties,
+                        uint32_t* count);
+LayerRef GetLayerRef(const char* name);
+
 }  // namespace vulkan
 
 #endif  // LIBVULKAN_LOADER_H