layers_extensions.cpp revision 1a7eb59c2a7907ea4c4925c6d13d66e53fb60339
1/* 2 * Copyright 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "layers_extensions.h" 18 19#include <alloca.h> 20#include <dirent.h> 21#include <dlfcn.h> 22#include <mutex> 23#include <sys/prctl.h> 24#include <string> 25#include <string.h> 26#include <vector> 27 28#include <android-base/strings.h> 29#include <cutils/properties.h> 30#include <log/log.h> 31#include <ziparchive/zip_archive.h> 32 33#include <vulkan/vulkan_loader_data.h> 34 35// TODO(jessehall): The whole way we deal with extensions is pretty hokey, and 36// not a good long-term solution. Having a hard-coded enum of extensions is 37// bad, of course. Representing sets of extensions (requested, supported, etc.) 38// as a bitset isn't necessarily bad, if the mapping from extension to bit were 39// dynamic. Need to rethink this completely when there's a little more time. 40 41// TODO(jessehall): This file currently builds up global data structures as it 42// loads, and never cleans them up. This means we're doing heap allocations 43// without going through an app-provided allocator, but worse, we'll leak those 44// allocations if the loader is unloaded. 45// 46// We should allocate "enough" BSS space, and suballocate from there. Will 47// probably want to intern strings, etc., and will need some custom/manual data 48// structures. 49 50namespace vulkan { 51namespace api { 52 53struct Layer { 54 VkLayerProperties properties; 55 size_t library_idx; 56 57 // true if the layer intercepts vkCreateDevice and device commands 58 bool is_global; 59 60 std::vector<VkExtensionProperties> instance_extensions; 61 std::vector<VkExtensionProperties> device_extensions; 62}; 63 64namespace { 65 66const char kSystemLayerLibraryDir[] = "/data/local/debug/vulkan"; 67 68class LayerLibrary { 69 public: 70 LayerLibrary(const std::string& path) 71 : path_(path), dlhandle_(nullptr), refcount_(0) {} 72 73 LayerLibrary(LayerLibrary&& other) 74 : path_(std::move(other.path_)), 75 dlhandle_(other.dlhandle_), 76 refcount_(other.refcount_) { 77 other.dlhandle_ = nullptr; 78 other.refcount_ = 0; 79 } 80 81 LayerLibrary(const LayerLibrary&) = delete; 82 LayerLibrary& operator=(const LayerLibrary&) = delete; 83 84 // these are thread-safe 85 bool Open(); 86 void Close(); 87 88 bool EnumerateLayers(size_t library_idx, 89 std::vector<Layer>& instance_layers) const; 90 91 void* GetGPA(const Layer& layer, 92 const char* gpa_name, 93 size_t gpa_name_len) const; 94 95 private: 96 const std::string path_; 97 98 std::mutex mutex_; 99 void* dlhandle_; 100 size_t refcount_; 101}; 102 103bool LayerLibrary::Open() { 104 std::lock_guard<std::mutex> lock(mutex_); 105 if (refcount_++ == 0) { 106 ALOGV("opening layer library '%s'", path_.c_str()); 107 dlhandle_ = dlopen(path_.c_str(), RTLD_NOW | RTLD_LOCAL); 108 if (!dlhandle_) { 109 ALOGE("failed to load layer library '%s': %s", path_.c_str(), 110 dlerror()); 111 refcount_ = 0; 112 return false; 113 } 114 } 115 return true; 116} 117 118void LayerLibrary::Close() { 119 std::lock_guard<std::mutex> lock(mutex_); 120 if (--refcount_ == 0) { 121 ALOGV("closing layer library '%s'", path_.c_str()); 122 dlclose(dlhandle_); 123 dlhandle_ = nullptr; 124 } 125} 126 127bool LayerLibrary::EnumerateLayers(size_t library_idx, 128 std::vector<Layer>& instance_layers) const { 129 PFN_vkEnumerateInstanceLayerProperties enumerate_instance_layers = 130 reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>( 131 dlsym(dlhandle_, "vkEnumerateInstanceLayerProperties")); 132 PFN_vkEnumerateInstanceExtensionProperties enumerate_instance_extensions = 133 reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties>( 134 dlsym(dlhandle_, "vkEnumerateInstanceExtensionProperties")); 135 if (!enumerate_instance_layers || !enumerate_instance_extensions) { 136 ALOGE("layer library '%s' missing some instance enumeration functions", 137 path_.c_str()); 138 return false; 139 } 140 141 // device functions are optional 142 PFN_vkEnumerateDeviceLayerProperties enumerate_device_layers = 143 reinterpret_cast<PFN_vkEnumerateDeviceLayerProperties>( 144 dlsym(dlhandle_, "vkEnumerateDeviceLayerProperties")); 145 PFN_vkEnumerateDeviceExtensionProperties enumerate_device_extensions = 146 reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>( 147 dlsym(dlhandle_, "vkEnumerateDeviceExtensionProperties")); 148 149 // get layer counts 150 uint32_t num_instance_layers = 0; 151 uint32_t num_device_layers = 0; 152 VkResult result = enumerate_instance_layers(&num_instance_layers, nullptr); 153 if (result != VK_SUCCESS || !num_instance_layers) { 154 if (result != VK_SUCCESS) { 155 ALOGE( 156 "vkEnumerateInstanceLayerProperties failed for library '%s': " 157 "%d", 158 path_.c_str(), result); 159 } 160 return false; 161 } 162 if (enumerate_device_layers) { 163 result = enumerate_device_layers(VK_NULL_HANDLE, &num_device_layers, 164 nullptr); 165 if (result != VK_SUCCESS) { 166 ALOGE( 167 "vkEnumerateDeviceLayerProperties failed for library '%s': %d", 168 path_.c_str(), result); 169 return false; 170 } 171 } 172 173 // get layer properties 174 VkLayerProperties* properties = static_cast<VkLayerProperties*>(alloca( 175 (num_instance_layers + num_device_layers) * sizeof(VkLayerProperties))); 176 result = enumerate_instance_layers(&num_instance_layers, properties); 177 if (result != VK_SUCCESS) { 178 ALOGE("vkEnumerateInstanceLayerProperties failed for library '%s': %d", 179 path_.c_str(), result); 180 return false; 181 } 182 if (num_device_layers > 0) { 183 result = enumerate_device_layers(VK_NULL_HANDLE, &num_device_layers, 184 properties + num_instance_layers); 185 if (result != VK_SUCCESS) { 186 ALOGE( 187 "vkEnumerateDeviceLayerProperties failed for library '%s': %d", 188 path_.c_str(), result); 189 return false; 190 } 191 } 192 193 // append layers to instance_layers 194 size_t prev_num_instance_layers = instance_layers.size(); 195 instance_layers.reserve(prev_num_instance_layers + num_instance_layers); 196 for (size_t i = 0; i < num_instance_layers; i++) { 197 const VkLayerProperties& props = properties[i]; 198 199 Layer layer; 200 layer.properties = props; 201 layer.library_idx = library_idx; 202 layer.is_global = false; 203 204 uint32_t count = 0; 205 result = 206 enumerate_instance_extensions(props.layerName, &count, nullptr); 207 if (result != VK_SUCCESS) { 208 ALOGE( 209 "vkEnumerateInstanceExtensionProperties(\"%s\") failed for " 210 "library '%s': %d", 211 props.layerName, path_.c_str(), result); 212 instance_layers.resize(prev_num_instance_layers); 213 return false; 214 } 215 layer.instance_extensions.resize(count); 216 result = enumerate_instance_extensions( 217 props.layerName, &count, layer.instance_extensions.data()); 218 if (result != VK_SUCCESS) { 219 ALOGE( 220 "vkEnumerateInstanceExtensionProperties(\"%s\") failed for " 221 "library '%s': %d", 222 props.layerName, path_.c_str(), result); 223 instance_layers.resize(prev_num_instance_layers); 224 return false; 225 } 226 227 for (size_t j = 0; j < num_device_layers; j++) { 228 const auto& dev_props = properties[num_instance_layers + j]; 229 if (memcmp(&props, &dev_props, sizeof(props)) == 0) { 230 layer.is_global = true; 231 break; 232 } 233 } 234 235 if (layer.is_global && enumerate_device_extensions) { 236 result = enumerate_device_extensions( 237 VK_NULL_HANDLE, props.layerName, &count, nullptr); 238 if (result != VK_SUCCESS) { 239 ALOGE( 240 "vkEnumerateDeviceExtensionProperties(\"%s\") failed for " 241 "library '%s': %d", 242 props.layerName, path_.c_str(), result); 243 instance_layers.resize(prev_num_instance_layers); 244 return false; 245 } 246 layer.device_extensions.resize(count); 247 result = enumerate_device_extensions( 248 VK_NULL_HANDLE, props.layerName, &count, 249 layer.device_extensions.data()); 250 if (result != VK_SUCCESS) { 251 ALOGE( 252 "vkEnumerateDeviceExtensionProperties(\"%s\") failed for " 253 "library '%s': %d", 254 props.layerName, path_.c_str(), result); 255 instance_layers.resize(prev_num_instance_layers); 256 return false; 257 } 258 } 259 260 instance_layers.push_back(layer); 261 ALOGD("added %s layer '%s' from library '%s'", 262 (layer.is_global) ? "global" : "instance", props.layerName, 263 path_.c_str()); 264 } 265 266 return true; 267} 268 269void* LayerLibrary::GetGPA(const Layer& layer, 270 const char* gpa_name, 271 size_t gpa_name_len) const { 272 void* gpa; 273 size_t layer_name_len = 274 std::max(size_t{2}, strlen(layer.properties.layerName)); 275 char* name = static_cast<char*>(alloca(layer_name_len + gpa_name_len + 1)); 276 strcpy(name, layer.properties.layerName); 277 strcpy(name + layer_name_len, gpa_name); 278 if (!(gpa = dlsym(dlhandle_, name))) { 279 strcpy(name, "vk"); 280 strcpy(name + 2, gpa_name); 281 gpa = dlsym(dlhandle_, name); 282 } 283 return gpa; 284} 285 286// ---------------------------------------------------------------------------- 287 288std::vector<LayerLibrary> g_layer_libraries; 289std::vector<Layer> g_instance_layers; 290 291void AddLayerLibrary(const std::string& path) { 292 LayerLibrary library(path); 293 if (!library.Open()) 294 return; 295 296 if (!library.EnumerateLayers(g_layer_libraries.size(), g_instance_layers)) { 297 library.Close(); 298 return; 299 } 300 301 library.Close(); 302 303 g_layer_libraries.emplace_back(std::move(library)); 304} 305 306template <typename Functor> 307void ForEachFileInDir(const std::string& dirname, Functor functor) { 308 auto dir_deleter = [](DIR* handle) { closedir(handle); }; 309 std::unique_ptr<DIR, decltype(dir_deleter)> dir(opendir(dirname.c_str()), 310 dir_deleter); 311 if (!dir) { 312 // It's normal for some search directories to not exist, especially 313 // /data/local/debug/vulkan. 314 int err = errno; 315 ALOGW_IF(err != ENOENT, "failed to open layer directory '%s': %s", 316 dirname.c_str(), strerror(err)); 317 return; 318 } 319 ALOGD("searching for layers in '%s'", dirname.c_str()); 320 dirent* entry; 321 while ((entry = readdir(dir.get())) != nullptr) 322 functor(entry->d_name); 323} 324 325template <typename Functor> 326void ForEachFileInZip(const std::string& zipname, 327 const std::string& dir_in_zip, 328 Functor functor) { 329 int32_t err; 330 ZipArchiveHandle zip = nullptr; 331 if ((err = OpenArchive(zipname.c_str(), &zip)) != 0) { 332 ALOGE("failed to open apk '%s': %d", zipname.c_str(), err); 333 return; 334 } 335 std::string prefix(dir_in_zip + "/"); 336 const ZipString prefix_str(prefix.c_str()); 337 void* iter_cookie = nullptr; 338 if ((err = StartIteration(zip, &iter_cookie, &prefix_str, nullptr)) != 0) { 339 ALOGE("failed to iterate entries in apk '%s': %d", zipname.c_str(), 340 err); 341 CloseArchive(zip); 342 return; 343 } 344 ALOGD("searching for layers in '%s!/%s'", zipname.c_str(), 345 dir_in_zip.c_str()); 346 ZipEntry entry; 347 ZipString name; 348 while (Next(iter_cookie, &entry, &name) == 0) { 349 std::string filename( 350 reinterpret_cast<const char*>(name.name) + prefix.length(), 351 name.name_length - prefix.length()); 352 // only enumerate direct entries of the directory, not subdirectories 353 if (filename.find('/') == filename.npos) 354 functor(filename); 355 } 356 EndIteration(iter_cookie); 357 CloseArchive(zip); 358} 359 360template <typename Functor> 361void ForEachFileInPath(const std::string& path, Functor functor) { 362 size_t zip_pos = path.find("!/"); 363 if (zip_pos == std::string::npos) { 364 ForEachFileInDir(path, functor); 365 } else { 366 ForEachFileInZip(path.substr(0, zip_pos), path.substr(zip_pos + 2), 367 functor); 368 } 369} 370 371void DiscoverLayersInPathList(const std::string& pathstr) { 372 std::vector<std::string> paths = android::base::Split(pathstr, ":"); 373 for (const auto& path : paths) { 374 ForEachFileInPath(path, [&](const std::string& filename) { 375 if (android::base::StartsWith(filename, "libVkLayer") && 376 android::base::EndsWith(filename, ".so")) { 377 AddLayerLibrary(path + "/" + filename); 378 } 379 }); 380 } 381} 382 383const VkExtensionProperties* FindExtension( 384 const std::vector<VkExtensionProperties>& extensions, 385 const char* name) { 386 auto it = std::find_if(extensions.cbegin(), extensions.cend(), 387 [=](const VkExtensionProperties& ext) { 388 return (strcmp(ext.extensionName, name) == 0); 389 }); 390 return (it != extensions.cend()) ? &*it : nullptr; 391} 392 393void* GetLayerGetProcAddr(const Layer& layer, 394 const char* gpa_name, 395 size_t gpa_name_len) { 396 const LayerLibrary& library = g_layer_libraries[layer.library_idx]; 397 return library.GetGPA(layer, gpa_name, gpa_name_len); 398} 399 400} // anonymous namespace 401 402void DiscoverLayers() { 403 if (property_get_bool("ro.debuggable", false) && 404 prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { 405 DiscoverLayersInPathList(kSystemLayerLibraryDir); 406 } 407 if (!LoaderData::GetInstance().layer_path.empty()) 408 DiscoverLayersInPathList(LoaderData::GetInstance().layer_path); 409} 410 411uint32_t GetLayerCount() { 412 return static_cast<uint32_t>(g_instance_layers.size()); 413} 414 415const Layer& GetLayer(uint32_t index) { 416 return g_instance_layers[index]; 417} 418 419const Layer* FindLayer(const char* name) { 420 auto layer = 421 std::find_if(g_instance_layers.cbegin(), g_instance_layers.cend(), 422 [=](const Layer& entry) { 423 return strcmp(entry.properties.layerName, name) == 0; 424 }); 425 return (layer != g_instance_layers.cend()) ? &*layer : nullptr; 426} 427 428const VkLayerProperties& GetLayerProperties(const Layer& layer) { 429 return layer.properties; 430} 431 432bool IsLayerGlobal(const Layer& layer) { 433 return layer.is_global; 434} 435 436const VkExtensionProperties* GetLayerInstanceExtensions(const Layer& layer, 437 uint32_t& count) { 438 count = static_cast<uint32_t>(layer.instance_extensions.size()); 439 return layer.instance_extensions.data(); 440} 441 442const VkExtensionProperties* GetLayerDeviceExtensions(const Layer& layer, 443 uint32_t& count) { 444 count = static_cast<uint32_t>(layer.device_extensions.size()); 445 return layer.device_extensions.data(); 446} 447 448const VkExtensionProperties* FindLayerInstanceExtension(const Layer& layer, 449 const char* name) { 450 return FindExtension(layer.instance_extensions, name); 451} 452 453const VkExtensionProperties* FindLayerDeviceExtension(const Layer& layer, 454 const char* name) { 455 return FindExtension(layer.device_extensions, name); 456} 457 458LayerRef GetLayerRef(const Layer& layer) { 459 LayerLibrary& library = g_layer_libraries[layer.library_idx]; 460 return LayerRef((library.Open()) ? &layer : nullptr); 461} 462 463LayerRef::LayerRef(const Layer* layer) : layer_(layer) {} 464 465LayerRef::~LayerRef() { 466 if (layer_) { 467 LayerLibrary& library = g_layer_libraries[layer_->library_idx]; 468 library.Close(); 469 } 470} 471 472LayerRef::LayerRef(LayerRef&& other) : layer_(other.layer_) { 473 other.layer_ = nullptr; 474} 475 476PFN_vkGetInstanceProcAddr LayerRef::GetGetInstanceProcAddr() const { 477 return layer_ ? reinterpret_cast<PFN_vkGetInstanceProcAddr>( 478 GetLayerGetProcAddr(*layer_, "GetInstanceProcAddr", 19)) 479 : nullptr; 480} 481 482PFN_vkGetDeviceProcAddr LayerRef::GetGetDeviceProcAddr() const { 483 return layer_ ? reinterpret_cast<PFN_vkGetDeviceProcAddr>( 484 GetLayerGetProcAddr(*layer_, "GetDeviceProcAddr", 17)) 485 : nullptr; 486} 487 488} // namespace api 489} // namespace vulkan 490