| /* |
| * 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 ATRACE_TAG ATRACE_TAG_GRAPHICS |
| |
| #include "layers_extensions.h" |
| |
| #include <alloca.h> |
| #include <dirent.h> |
| #include <dlfcn.h> |
| #include <string.h> |
| #include <sys/prctl.h> |
| #include <unistd.h> |
| |
| #include <mutex> |
| #include <string> |
| #include <vector> |
| |
| #include <android/dlext.h> |
| #include <android-base/strings.h> |
| #include <cutils/properties.h> |
| #include <graphicsenv/GraphicsEnv.h> |
| #include <log/log.h> |
| #include <nativebridge/native_bridge.h> |
| #include <nativeloader/native_loader.h> |
| #include <utils/Trace.h> |
| #include <ziparchive/zip_archive.h> |
| |
| #include <com_android_graphics_libvulkan_flags.h> |
| using namespace com::android::graphics::libvulkan; |
| |
| #include <VulkanProperties.sysprop.h> |
| using namespace android::sysprop; |
| |
| // TODO(b/143296676): 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 |
| // allocations if the loader is unloaded. |
| // |
| // We should allocate "enough" BSS space, and suballocate from there. Will |
| // probably want to intern strings, etc., and will need some custom/manual data |
| // structures. |
| |
| namespace vulkan { |
| namespace api { |
| |
| enum LayerType { |
| IMPLICIT, |
| EXPLICIT, |
| PLATFORM, |
| OEM, |
| }; |
| |
| struct Layer { |
| VkLayerProperties properties; |
| size_t library_idx; |
| |
| LayerType type; |
| |
| // true if the layer intercepts vkCreateDevice and device commands |
| bool is_global; |
| |
| std::vector<VkExtensionProperties> instance_extensions; |
| std::vector<VkExtensionProperties> device_extensions; |
| }; |
| |
| namespace { |
| |
| const char kSystemDebugLayerLibraryDir[] = "/data/local/debug/vulkan"; |
| #if defined(__LP64__) |
| const char kSystemPlatformLayerLibraryDir[] = "/system/lib64/vulkan"; |
| const char kSystemOEMLayerLibraryDir[] = "/product/lib64/vulkan"; |
| #else |
| const char kSystemPlatformLayerLibraryDir[] = "/system/lib/vulkan"; |
| const char kSystemOEMLayerLibraryDir[] = "/product/lib/vulkan"; |
| #endif |
| |
| class LayerLibrary { |
| public: |
| explicit LayerLibrary(const std::string& path, |
| const std::string& filename) |
| : path_(path), |
| filename_(filename), |
| dlhandle_(nullptr), |
| native_bridge_(false), |
| opened_with_native_loader_(false), |
| refcount_(0) {} |
| |
| LayerLibrary(LayerLibrary&& other) noexcept |
| : path_(std::move(other.path_)), |
| filename_(std::move(other.filename_)), |
| dlhandle_(other.dlhandle_), |
| native_bridge_(other.native_bridge_), |
| opened_with_native_loader_(other.opened_with_native_loader_), |
| refcount_(other.refcount_) { |
| other.dlhandle_ = nullptr; |
| other.refcount_ = 0; |
| } |
| |
| LayerLibrary(const LayerLibrary&) = delete; |
| LayerLibrary& operator=(const LayerLibrary&) = delete; |
| |
| // these are thread-safe |
| bool Open(); |
| void Close(); |
| |
| bool EnumerateLayers(size_t library_idx, |
| std::vector<Layer>& instance_layers, |
| LayerType type) const; |
| |
| void* GetGPA(const Layer& layer, const std::string_view gpa_name) const; |
| |
| const std::string GetFilename() { return filename_; } |
| |
| private: |
| // TODO(b/79940628): remove that adapter when we could use NativeBridgeGetTrampoline |
| // for native libraries. |
| template<typename Func = void*> |
| Func GetTrampoline(const char* name) const { |
| if (native_bridge_) { |
| return reinterpret_cast<Func>(android::NativeBridgeGetTrampoline( |
| dlhandle_, name, nullptr, 0)); |
| } |
| return reinterpret_cast<Func>(dlsym(dlhandle_, name)); |
| } |
| |
| const std::string path_; |
| |
| // Track the filename alone so we can detect duplicates |
| const std::string filename_; |
| |
| std::mutex mutex_; |
| void* dlhandle_; |
| bool native_bridge_; |
| bool opened_with_native_loader_; |
| size_t refcount_; |
| }; |
| |
| bool LayerLibrary::Open() { |
| std::lock_guard<std::mutex> lock(mutex_); |
| if (refcount_++ == 0) { |
| ALOGV("opening layer library '%s'", path_.c_str()); |
| // Libraries in the system layer library dir can't be loaded into |
| // the application namespace. That causes compatibility problems, since |
| // any symbol dependencies will be resolved by system libraries. They |
| // can't safely use libc++_shared, for example. Which is one reason |
| // (among several) we only allow them in non-user builds. |
| auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace(); |
| if (app_namespace && |
| !(android::base::StartsWith(path_, kSystemDebugLayerLibraryDir) || |
| android::base::StartsWith(path_, |
| kSystemPlatformLayerLibraryDir) || |
| android::base::StartsWith(path_, kSystemOEMLayerLibraryDir))) { |
| char* error_msg = nullptr; |
| dlhandle_ = android::OpenNativeLibraryInNamespace( |
| app_namespace, path_.c_str(), &native_bridge_, &error_msg); |
| if (!dlhandle_) { |
| ALOGE("failed to load layer library '%s': %s", path_.c_str(), error_msg); |
| android::NativeLoaderFreeErrorMessage(error_msg); |
| refcount_ = 0; |
| return false; |
| } |
| opened_with_native_loader_ = true; |
| } else { |
| dlhandle_ = dlopen(path_.c_str(), RTLD_NOW | RTLD_LOCAL); |
| if (!dlhandle_) { |
| ALOGE("failed to load layer library '%s': %s", path_.c_str(), |
| dlerror()); |
| refcount_ = 0; |
| return false; |
| } |
| opened_with_native_loader_ = false; |
| } |
| } |
| return true; |
| } |
| |
| void LayerLibrary::Close() { |
| std::lock_guard<std::mutex> lock(mutex_); |
| if (--refcount_ == 0) { |
| ALOGV("closing layer library '%s'", path_.c_str()); |
| // we close the .so same way as we opened. It's importain, because |
| // android::CloseNativeLibrary lives in libnativeloader.so, which is |
| // not accessible for early loaded services like SurfaceFlinger |
| if (opened_with_native_loader_) { |
| char* error_msg = nullptr; |
| if (!android::CloseNativeLibrary(dlhandle_, native_bridge_, &error_msg)) { |
| ALOGE("failed to unload library '%s': %s", path_.c_str(), error_msg); |
| android::NativeLoaderFreeErrorMessage(error_msg); |
| refcount_++; |
| return; |
| } |
| } else { |
| if (dlclose(dlhandle_) != 0) { |
| ALOGE("failed to unload library '%s': %s", path_.c_str(), dlerror()); |
| refcount_++; |
| return; |
| } |
| } |
| dlhandle_ = nullptr; |
| } |
| } |
| |
| bool LayerLibrary::EnumerateLayers(size_t library_idx, |
| std::vector<Layer>& instance_layers, |
| LayerType type) const { |
| PFN_vkEnumerateInstanceLayerProperties enumerate_instance_layers = |
| GetTrampoline<PFN_vkEnumerateInstanceLayerProperties>( |
| "vkEnumerateInstanceLayerProperties"); |
| PFN_vkEnumerateInstanceExtensionProperties enumerate_instance_extensions = |
| GetTrampoline<PFN_vkEnumerateInstanceExtensionProperties>( |
| "vkEnumerateInstanceExtensionProperties"); |
| if (!enumerate_instance_layers || !enumerate_instance_extensions) { |
| ALOGE("layer library '%s' missing some instance enumeration functions", |
| path_.c_str()); |
| return false; |
| } |
| |
| // device functions are optional |
| PFN_vkEnumerateDeviceLayerProperties enumerate_device_layers = |
| GetTrampoline<PFN_vkEnumerateDeviceLayerProperties>( |
| "vkEnumerateDeviceLayerProperties"); |
| PFN_vkEnumerateDeviceExtensionProperties enumerate_device_extensions = |
| GetTrampoline<PFN_vkEnumerateDeviceExtensionProperties>( |
| "vkEnumerateDeviceExtensionProperties"); |
| |
| // get layer counts |
| uint32_t num_instance_layers = 0; |
| uint32_t num_device_layers = 0; |
| VkResult result = enumerate_instance_layers(&num_instance_layers, nullptr); |
| if (result != VK_SUCCESS || !num_instance_layers) { |
| if (result != VK_SUCCESS) { |
| ALOGE( |
| "vkEnumerateInstanceLayerProperties failed for library '%s': " |
| "%d", |
| path_.c_str(), result); |
| } |
| return false; |
| } |
| if (enumerate_device_layers) { |
| result = enumerate_device_layers(VK_NULL_HANDLE, &num_device_layers, |
| nullptr); |
| if (result != VK_SUCCESS) { |
| ALOGE( |
| "vkEnumerateDeviceLayerProperties failed for library '%s': %d", |
| path_.c_str(), result); |
| return false; |
| } |
| } |
| |
| // get layer properties |
| std::vector<VkLayerProperties> properties(num_instance_layers + num_device_layers); |
| result = enumerate_instance_layers(&num_instance_layers, properties.data()); |
| if (result != VK_SUCCESS) { |
| ALOGE("vkEnumerateInstanceLayerProperties failed for library '%s': %d", |
| path_.c_str(), result); |
| return false; |
| } |
| if (num_device_layers > 0) { |
| result = enumerate_device_layers(VK_NULL_HANDLE, &num_device_layers, |
| &properties[num_instance_layers]); |
| if (result != VK_SUCCESS) { |
| ALOGE( |
| "vkEnumerateDeviceLayerProperties failed for library '%s': %d", |
| path_.c_str(), result); |
| return false; |
| } |
| } |
| |
| // append layers to instance_layers |
| size_t prev_num_instance_layers = instance_layers.size(); |
| instance_layers.reserve(prev_num_instance_layers + num_instance_layers); |
| for (size_t i = 0; i < num_instance_layers; i++) { |
| const VkLayerProperties& props = properties[i]; |
| |
| Layer layer; |
| layer.properties = props; |
| layer.library_idx = library_idx; |
| layer.is_global = false; |
| layer.type = type; |
| |
| uint32_t count = 0; |
| result = |
| enumerate_instance_extensions(props.layerName, &count, nullptr); |
| if (result != VK_SUCCESS) { |
| ALOGE( |
| "vkEnumerateInstanceExtensionProperties(\"%s\") failed for " |
| "library '%s': %d", |
| props.layerName, path_.c_str(), result); |
| instance_layers.resize(prev_num_instance_layers); |
| return false; |
| } |
| layer.instance_extensions.resize(count); |
| result = enumerate_instance_extensions( |
| props.layerName, &count, layer.instance_extensions.data()); |
| if (result != VK_SUCCESS) { |
| ALOGE( |
| "vkEnumerateInstanceExtensionProperties(\"%s\") failed for " |
| "library '%s': %d", |
| props.layerName, path_.c_str(), result); |
| instance_layers.resize(prev_num_instance_layers); |
| return false; |
| } |
| |
| for (size_t j = 0; j < num_device_layers; j++) { |
| const auto& dev_props = properties[num_instance_layers + j]; |
| if (memcmp(&props, &dev_props, sizeof(props)) == 0) { |
| layer.is_global = true; |
| break; |
| } |
| } |
| |
| if (layer.is_global && enumerate_device_extensions) { |
| result = enumerate_device_extensions( |
| VK_NULL_HANDLE, props.layerName, &count, nullptr); |
| if (result != VK_SUCCESS) { |
| ALOGE( |
| "vkEnumerateDeviceExtensionProperties(\"%s\") failed for " |
| "library '%s': %d", |
| props.layerName, path_.c_str(), result); |
| instance_layers.resize(prev_num_instance_layers); |
| return false; |
| } |
| layer.device_extensions.resize(count); |
| result = enumerate_device_extensions( |
| VK_NULL_HANDLE, props.layerName, &count, |
| layer.device_extensions.data()); |
| if (result != VK_SUCCESS) { |
| ALOGE( |
| "vkEnumerateDeviceExtensionProperties(\"%s\") failed for " |
| "library '%s': %d", |
| props.layerName, path_.c_str(), result); |
| instance_layers.resize(prev_num_instance_layers); |
| return false; |
| } |
| } |
| |
| instance_layers.push_back(layer); |
| ALOGD("added %s layer '%s' from library '%s'", |
| (layer.is_global) ? "global" : "instance", props.layerName, |
| path_.c_str()); |
| } |
| |
| return true; |
| } |
| |
| void* LayerLibrary::GetGPA(const Layer& layer, const std::string_view gpa_name) const { |
| std::string layer_name { layer.properties.layerName }; |
| if (void* gpa = GetTrampoline((layer_name.append(gpa_name).c_str()))) |
| return gpa; |
| return GetTrampoline((std::string {"vk"}.append(gpa_name)).c_str()); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| std::vector<LayerLibrary> g_layer_libraries; |
| std::vector<Layer> g_instance_layers; |
| |
| void AddLayerLibrary(const std::string& path, |
| const std::string& filename, |
| LayerType type) { |
| LayerLibrary library(path + "/" + filename, filename); |
| if (!library.Open()) |
| return; |
| |
| if (!library.EnumerateLayers(g_layer_libraries.size(), g_instance_layers, |
| type)) { |
| library.Close(); |
| return; |
| } |
| |
| library.Close(); |
| |
| g_layer_libraries.emplace_back(std::move(library)); |
| } |
| |
| template <typename Functor> |
| void ForEachFileInDir(const std::string& dirname, Functor functor) { |
| auto dir_deleter = [](DIR* handle) { closedir(handle); }; |
| std::unique_ptr<DIR, decltype(dir_deleter)> dir(opendir(dirname.c_str()), |
| dir_deleter); |
| if (!dir) { |
| // It's normal for some search directories to not exist, especially |
| // /data/local/debug/vulkan. |
| int err = errno; |
| ALOGW_IF(err != ENOENT, "failed to open layer directory '%s': %s", |
| dirname.c_str(), strerror(err)); |
| return; |
| } |
| ALOGD("searching for layers in '%s'", dirname.c_str()); |
| dirent* entry; |
| while ((entry = readdir(dir.get())) != nullptr) |
| functor(entry->d_name); |
| } |
| |
| template <typename Functor> |
| void ForEachFileInZip(const std::string& zipname, |
| const std::string& dir_in_zip, |
| Functor functor) { |
| static const size_t kPageSize = getpagesize(); |
| int32_t err; |
| ZipArchiveHandle zip = nullptr; |
| if ((err = OpenArchive(zipname.c_str(), &zip)) != 0) { |
| ALOGE("failed to open apk '%s': %d", zipname.c_str(), err); |
| return; |
| } |
| std::string prefix(dir_in_zip + "/"); |
| void* iter_cookie = nullptr; |
| if ((err = StartIteration(zip, &iter_cookie, prefix, "")) != 0) { |
| ALOGE("failed to iterate entries in apk '%s': %d", zipname.c_str(), |
| err); |
| CloseArchive(zip); |
| return; |
| } |
| ALOGD("searching for layers in '%s!/%s'", zipname.c_str(), |
| dir_in_zip.c_str()); |
| ZipEntry entry; |
| std::string name; |
| while (Next(iter_cookie, &entry, &name) == 0) { |
| std::string filename(name.substr(prefix.length())); |
| // only enumerate direct entries of the directory, not subdirectories |
| if (filename.find('/') != filename.npos) |
| continue; |
| // Check whether it *may* be possible to load the library directly from |
| // the APK. Loading still may fail for other reasons, but this at least |
| // lets us avoid failed-to-load log messages in the typical case of |
| // compressed and/or unaligned libraries. |
| if (entry.method != kCompressStored || entry.offset % kPageSize != 0) |
| continue; |
| functor(filename); |
| } |
| EndIteration(iter_cookie); |
| CloseArchive(zip); |
| } |
| |
| template <typename Functor> |
| void ForEachFileInPath(const std::string& path, Functor functor) { |
| size_t zip_pos = path.find("!/"); |
| if (zip_pos == std::string::npos) { |
| ForEachFileInDir(path, functor); |
| } else { |
| ForEachFileInZip(path.substr(0, zip_pos), path.substr(zip_pos + 2), |
| functor); |
| } |
| } |
| |
| void DiscoverLayersInPathList(const std::string& pathstr, LayerType type) { |
| ATRACE_CALL(); |
| |
| std::vector<std::string> paths = android::base::Split(pathstr, ":"); |
| for (const auto& path : paths) { |
| ForEachFileInPath(path, [&](const std::string& filename) { |
| if (android::base::StartsWith(filename, "libVkLayer") && |
| android::base::EndsWith(filename, ".so")) { |
| |
| // Check to ensure we haven't seen this layer already |
| // Let the first instance of the shared object be enumerated |
| // We're searching for layers in following order: |
| // 1. system path |
| // 2. libraryPermittedPath (if enabled) |
| // 3. libraryPath |
| |
| bool duplicate = false; |
| for (auto& layer : g_layer_libraries) { |
| if (layer.GetFilename() == filename) { |
| ALOGV("Skipping duplicate layer %s in %s", |
| filename.c_str(), path.c_str()); |
| duplicate = true; |
| } |
| } |
| |
| if (!duplicate) |
| AddLayerLibrary(path, filename, type); |
| } |
| }); |
| } |
| } |
| |
| void DiscoverLayersFromProperty(const std::string& path, |
| const std::string& librariesprop, |
| LayerType type) { |
| ATRACE_CALL(); |
| |
| std::vector<std::string> libraries = |
| android::base::Split(librariesprop, ":"); |
| for (const auto& filename : libraries) { |
| if (android::base::StartsWith(filename, "libVkLayer") && |
| android::base::EndsWith(filename, ".so")) { |
| // Check to ensure we haven't seen this layer already |
| // (e.g. an IMPLICIT or EXPLICIT layer) |
| |
| bool duplicate = false; |
| for (auto& layer : g_layer_libraries) { |
| if (layer.GetFilename() == filename) { |
| ALOGV("Skipping duplicate layer %s in %s", filename.c_str(), |
| path.c_str()); |
| duplicate = true; |
| } |
| } |
| |
| if (!duplicate) |
| AddLayerLibrary(path, filename, type); |
| } |
| } |
| } |
| |
| const VkExtensionProperties* FindExtension( |
| const std::vector<VkExtensionProperties>& extensions, |
| const char* name) { |
| auto it = std::find_if(extensions.cbegin(), extensions.cend(), |
| [=](const VkExtensionProperties& ext) { |
| return (strcmp(ext.extensionName, name) == 0); |
| }); |
| return (it != extensions.cend()) ? &*it : nullptr; |
| } |
| |
| void* GetLayerGetProcAddr(const Layer& layer, |
| const std::string_view gpa_name) { |
| const LayerLibrary& library = g_layer_libraries[layer.library_idx]; |
| return library.GetGPA(layer, gpa_name); |
| } |
| |
| } // anonymous namespace |
| |
| /* This function is used to discover all of the implicit, explicit, platform, |
| * and OEM layers that should be used for the process (typically an application |
| * APK, but potentially a system process such as SurfaceFlinger). |
| * |
| * Android's security model restricts usage of implicit layers (a.k.a. "debug |
| * layers") to one of the following cases for the target app: |
| * |
| * - The target app is debuggable |
| * - The target app is run on a userdebug build of the operating system which |
| * grants root access |
| * - The target app's manifest file includes the following meta-data element |
| * (only applies to apps that target Android 11 (API level 30) or higher): |
| * <meta-data android:name="com.android.graphics.injectLayers.enable" |
| * android:value="true" /> |
| * |
| * Implicit layers can be globally enabled until the next reboot, using the |
| * following command: |
| * |
| * - adb shell setprop debug.vulkan.layers <layer1:layer2:layerN> |
| * |
| * Implicit layers can be enabled for a single target application, using the |
| * "adb shell settings put global" command to configure the following settings |
| * (that persist across reboots): |
| * |
| * - enable_gpu_debug_layers 1 |
| * - gpu_debug_app <package_name> |
| * - gpu_debug_layers <layer1:layer2:layerN> |
| * - gpu_debug_layer_app <package1:package2:packageN> |
| */ |
| void DiscoverLayers() { |
| ATRACE_CALL(); |
| |
| // Look in the original Android "implicit" layer (a.k.a. "debug layer") path |
| // for implicit layers, configured by the following settings (as described |
| // above): enable_gpu_debug_layers, gpu_debug_app, gpu_debug_layers |
| if (android::GraphicsEnv::getInstance().isDebuggable()) { |
| DiscoverLayersInPathList(kSystemDebugLayerLibraryDir, |
| LayerType::IMPLICIT); |
| } |
| // Look in the application's APK for "explicit" layers. Also look for |
| // "implicit" layers living in another APK(s), configured by all of the |
| // per-app settings described above. If configured, both APKs are returned |
| // by getLayerPaths(). |
| // |
| // TODO (b/455899446): Split treatment of IMPLICIT and EXPLICIT layers, |
| // which are currently treated the same in the code below. |
| if (!android::GraphicsEnv::getInstance().getLayerPaths().empty()) |
| DiscoverLayersInPathList( |
| android::GraphicsEnv::getInstance().getLayerPaths(), |
| LayerType::EXPLICIT); |
| // TODO: longer term, we may want to load/Open() OPLs in Zygote and |
| // inherit them into this process (i.e. not Open() and Close() them for each |
| // process). |
| if (flags::oem_and_platform_layers()) { |
| // The layer properties are colon-separated lists of layer filenames |
| std::string platformLayerLibraryNames = |
| VulkanProperties::platform_layers().value_or(""); |
| if (!platformLayerLibraryNames.empty()) { |
| DiscoverLayersFromProperty(kSystemPlatformLayerLibraryDir, |
| platformLayerLibraryNames, |
| LayerType::PLATFORM); |
| } |
| std::string oemLayerLibraryNames = |
| VulkanProperties::oem_layers().value_or(""); |
| if (!oemLayerLibraryNames.empty()) { |
| DiscoverLayersFromProperty(kSystemOEMLayerLibraryDir, |
| oemLayerLibraryNames, LayerType::OEM); |
| } |
| } |
| } |
| |
| /* Return the number of IMPLICIT and EXPLICIT Layer's (not PLATFORM nor OEM |
| * Layer's), that should be returned by vkEnumerateInstanceLayerProperties(). |
| * |
| * NOTE: This relies on all IMPLICIT and EXPLICIT Layer's being added to |
| * g_instance_layers before any PLATFORM or OEM Layer's. |
| */ |
| uint32_t GetEnumeratedLayerCount() { |
| uint32_t count = 0; |
| for (size_t i = 0; i < g_instance_layers.size(); i++) { |
| LayerType type = g_instance_layers[i].type; |
| if (type == LayerType::PLATFORM || type == LayerType::OEM) { |
| break; |
| } |
| count++; |
| } |
| return count; |
| } |
| |
| /* Return the number of PLATFORM and OEM Layer's (not IMPLICIT nor EXPLICIT |
| * Layer's), that LayerChain::ActivateLayers() should activate. |
| * |
| * NOTE: This relies on all IMPLICIT and EXPLICIT Layer's being added to |
| * g_instance_layers before any PLATFORM or OEM Layer's. |
| */ |
| uint32_t GetOemAndPlatformLayerCount() { |
| uint32_t count = g_instance_layers.size() - GetEnumeratedLayerCount(); |
| return count; |
| } |
| |
| const Layer& GetLayer(uint32_t index) { |
| return g_instance_layers[index]; |
| } |
| |
| const Layer* FindLayer(const char* name) { |
| auto layer = |
| std::find_if(g_instance_layers.cbegin(), g_instance_layers.cend(), |
| [=](const Layer& entry) { |
| return strcmp(entry.properties.layerName, name) == 0; |
| }); |
| return (layer != g_instance_layers.cend()) ? &*layer : nullptr; |
| } |
| |
| const VkLayerProperties& GetLayerProperties(const Layer& layer) { |
| return layer.properties; |
| } |
| |
| bool IsLayerGlobal(const Layer& layer) { |
| return layer.is_global; |
| } |
| |
| const VkExtensionProperties* GetLayerInstanceExtensions(const Layer& layer, |
| uint32_t& count) { |
| count = static_cast<uint32_t>(layer.instance_extensions.size()); |
| return layer.instance_extensions.data(); |
| } |
| |
| const VkExtensionProperties* GetLayerDeviceExtensions(const Layer& layer, |
| uint32_t& count) { |
| count = static_cast<uint32_t>(layer.device_extensions.size()); |
| return layer.device_extensions.data(); |
| } |
| |
| const VkExtensionProperties* FindLayerInstanceExtension(const Layer& layer, |
| const char* name) { |
| return FindExtension(layer.instance_extensions, name); |
| } |
| |
| const VkExtensionProperties* FindLayerDeviceExtension(const Layer& layer, |
| const char* name) { |
| return FindExtension(layer.device_extensions, name); |
| } |
| |
| LayerRef GetLayerRef(const Layer& layer) { |
| LayerLibrary& library = g_layer_libraries[layer.library_idx]; |
| return LayerRef((library.Open()) ? &layer : nullptr); |
| } |
| |
| LayerRef::LayerRef(const Layer* layer) : layer_(layer) {} |
| |
| LayerRef::~LayerRef() { |
| if (layer_) { |
| LayerLibrary& library = g_layer_libraries[layer_->library_idx]; |
| library.Close(); |
| } |
| } |
| |
| LayerRef::LayerRef(LayerRef&& other) noexcept : layer_(other.layer_) { |
| other.layer_ = nullptr; |
| } |
| |
| PFN_vkGetInstanceProcAddr LayerRef::GetGetInstanceProcAddr() const { |
| return layer_ ? reinterpret_cast<PFN_vkGetInstanceProcAddr>( |
| GetLayerGetProcAddr(*layer_, "GetInstanceProcAddr")) |
| : nullptr; |
| } |
| |
| PFN_vkGetDeviceProcAddr LayerRef::GetGetDeviceProcAddr() const { |
| return layer_ ? reinterpret_cast<PFN_vkGetDeviceProcAddr>( |
| GetLayerGetProcAddr(*layer_, "GetDeviceProcAddr")) |
| : nullptr; |
| } |
| |
| } // namespace api |
| } // namespace vulkan |