vulkan: Implement layer and extension enumeration
Change-Id: I485ebbe3e57da396d361f772793e1e89850c334c
(cherry picked from commit 4bee2c3f2fdff04f1eb437f24a7bcf841364d5b3)
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