layers_extensions.cpp revision bea09db9c2165f06771f3a3da423f4f85ac6347e
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// #define LOG_NDEBUG 0
18
19#include "layers_extensions.h"
20#include <alloca.h>
21#include <dirent.h>
22#include <dlfcn.h>
23#include <mutex>
24#include <sys/prctl.h>
25#include <string>
26#include <string.h>
27#include <vector>
28#include <log/log.h>
29#include <vulkan/vulkan_loader_data.h>
30
31// TODO(jessehall): The whole way we deal with extensions is pretty hokey, and
32// not a good long-term solution. Having a hard-coded enum of extensions is
33// bad, of course. Representing sets of extensions (requested, supported, etc.)
34// as a bitset isn't necessarily bad, if the mapping from extension to bit were
35// dynamic. Need to rethink this completely when there's a little more time.
36
37// TODO(jessehall): This file currently builds up global data structures as it
38// loads, and never cleans them up. This means we're doing heap allocations
39// without going through an app-provided allocator, but worse, we'll leak those
40// allocations if the loader is unloaded.
41//
42// We should allocate "enough" BSS space, and suballocate from there. Will
43// probably want to intern strings, etc., and will need some custom/manual data
44// structures.
45
46namespace vulkan {
47namespace api {
48
49struct Layer {
50    VkLayerProperties properties;
51    size_t library_idx;
52
53    bool is_global;
54
55    std::vector<VkExtensionProperties> instance_extensions;
56    std::vector<VkExtensionProperties> device_extensions;
57};
58
59namespace {
60
61class LayerLibrary {
62   public:
63    LayerLibrary(const std::string& path)
64        : path_(path), dlhandle_(nullptr), refcount_(0) {}
65
66    LayerLibrary(LayerLibrary&& other)
67        : path_(std::move(other.path_)),
68          dlhandle_(other.dlhandle_),
69          refcount_(other.refcount_) {
70        other.dlhandle_ = nullptr;
71        other.refcount_ = 0;
72    }
73
74    LayerLibrary(const LayerLibrary&) = delete;
75    LayerLibrary& operator=(const LayerLibrary&) = delete;
76
77    // these are thread-safe
78    bool Open();
79    void Close();
80
81    bool EnumerateLayers(size_t library_idx,
82                         std::vector<Layer>& instance_layers) const;
83
84    void* GetGPA(const Layer& layer,
85                 const char* gpa_name,
86                 size_t gpa_name_len) const;
87
88   private:
89    const std::string path_;
90
91    std::mutex mutex_;
92    void* dlhandle_;
93    size_t refcount_;
94};
95
96bool LayerLibrary::Open() {
97    std::lock_guard<std::mutex> lock(mutex_);
98
99    if (refcount_++ == 0) {
100        dlhandle_ = dlopen(path_.c_str(), RTLD_NOW | RTLD_LOCAL);
101        ALOGV("Opening library %s", path_.c_str());
102        if (!dlhandle_) {
103            ALOGE("failed to load layer library '%s': %s", path_.c_str(),
104                  dlerror());
105            refcount_ = 0;
106            return false;
107        }
108    }
109    ALOGV("Refcount on activate is %zu", refcount_);
110    return true;
111}
112
113void LayerLibrary::Close() {
114    std::lock_guard<std::mutex> lock(mutex_);
115
116    if (--refcount_ == 0) {
117        ALOGV("Closing library %s", path_.c_str());
118        dlclose(dlhandle_);
119        dlhandle_ = nullptr;
120    }
121    ALOGV("Refcount on destruction is %zu", refcount_);
122}
123
124bool LayerLibrary::EnumerateLayers(size_t library_idx,
125                                   std::vector<Layer>& instance_layers) const {
126    PFN_vkEnumerateInstanceLayerProperties enumerate_instance_layers =
127        reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(
128            dlsym(dlhandle_, "vkEnumerateInstanceLayerProperties"));
129    PFN_vkEnumerateInstanceExtensionProperties enumerate_instance_extensions =
130        reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties>(
131            dlsym(dlhandle_, "vkEnumerateInstanceExtensionProperties"));
132    if (!enumerate_instance_layers || !enumerate_instance_extensions) {
133        ALOGV("layer library '%s' misses some instance enumeraion functions",
134              path_.c_str());
135        return false;
136    }
137
138    // device functions are optional
139    PFN_vkEnumerateDeviceLayerProperties enumerate_device_layers =
140        reinterpret_cast<PFN_vkEnumerateDeviceLayerProperties>(
141            dlsym(dlhandle_, "vkEnumerateDeviceLayerProperties"));
142    PFN_vkEnumerateDeviceExtensionProperties enumerate_device_extensions =
143        reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(
144            dlsym(dlhandle_, "vkEnumerateDeviceExtensionProperties"));
145
146    // get layer counts
147    uint32_t num_instance_layers = 0;
148    uint32_t num_device_layers = 0;
149    VkResult result = enumerate_instance_layers(&num_instance_layers, nullptr);
150    if (result != VK_SUCCESS || !num_instance_layers) {
151        if (result != VK_SUCCESS) {
152            ALOGW(
153                "vkEnumerateInstanceLayerProperties failed for library '%s': "
154                "%d",
155                path_.c_str(), result);
156        }
157        return false;
158    }
159    if (enumerate_device_layers) {
160        result = enumerate_device_layers(VK_NULL_HANDLE, &num_device_layers,
161                                         nullptr);
162        if (result != VK_SUCCESS) {
163            ALOGW(
164                "vkEnumerateDeviceLayerProperties failed for library '%s': %d",
165                path_.c_str(), result);
166            return false;
167        }
168    }
169
170    // get layer properties
171    VkLayerProperties* properties = static_cast<VkLayerProperties*>(alloca(
172        (num_instance_layers + num_device_layers) * sizeof(VkLayerProperties)));
173    result = enumerate_instance_layers(&num_instance_layers, properties);
174    if (result != VK_SUCCESS) {
175        ALOGW("vkEnumerateInstanceLayerProperties failed for library '%s': %d",
176              path_.c_str(), result);
177        return false;
178    }
179    if (num_device_layers > 0) {
180        result = enumerate_device_layers(VK_NULL_HANDLE, &num_device_layers,
181                                         properties + num_instance_layers);
182        if (result != VK_SUCCESS) {
183            ALOGW(
184                "vkEnumerateDeviceLayerProperties failed for library '%s': %d",
185                path_.c_str(), result);
186            return false;
187        }
188    }
189
190    // append layers to instance_layers
191    size_t prev_num_instance_layers = instance_layers.size();
192    instance_layers.reserve(prev_num_instance_layers + num_instance_layers);
193    for (size_t i = 0; i < num_instance_layers; i++) {
194        const VkLayerProperties& props = properties[i];
195
196        Layer layer;
197        layer.properties = props;
198        layer.library_idx = library_idx;
199        layer.is_global = false;
200
201        uint32_t count = 0;
202        result =
203            enumerate_instance_extensions(props.layerName, &count, nullptr);
204        if (result != VK_SUCCESS) {
205            ALOGW(
206                "vkEnumerateInstanceExtensionProperties(%s) failed for library "
207                "'%s': %d",
208                props.layerName, path_.c_str(), result);
209            instance_layers.resize(prev_num_instance_layers);
210            return false;
211        }
212        layer.instance_extensions.resize(count);
213        result = enumerate_instance_extensions(
214            props.layerName, &count, layer.instance_extensions.data());
215        if (result != VK_SUCCESS) {
216            ALOGW(
217                "vkEnumerateInstanceExtensionProperties(%s) failed for library "
218                "'%s': %d",
219                props.layerName, path_.c_str(), result);
220            instance_layers.resize(prev_num_instance_layers);
221            return false;
222        }
223
224        for (size_t j = 0; j < num_device_layers; j++) {
225            const auto& dev_props = properties[num_instance_layers + j];
226            if (memcmp(&props, &dev_props, sizeof(props)) == 0) {
227                layer.is_global = true;
228                break;
229            }
230        }
231
232        if (layer.is_global && enumerate_device_extensions) {
233            result = enumerate_device_extensions(
234                VK_NULL_HANDLE, props.layerName, &count, nullptr);
235            if (result != VK_SUCCESS) {
236                ALOGW(
237                    "vkEnumerateDeviceExtensionProperties(%s) failed for "
238                    "library '%s': %d",
239                    props.layerName, path_.c_str(), result);
240                instance_layers.resize(prev_num_instance_layers);
241                return false;
242            }
243            layer.device_extensions.resize(count);
244            result = enumerate_device_extensions(
245                VK_NULL_HANDLE, props.layerName, &count,
246                layer.device_extensions.data());
247            if (result != VK_SUCCESS) {
248                ALOGW(
249                    "vkEnumerateDeviceExtensionProperties(%s) failed for "
250                    "library '%s': %d",
251                    props.layerName, path_.c_str(), result);
252                instance_layers.resize(prev_num_instance_layers);
253                return false;
254            }
255        }
256
257        instance_layers.push_back(layer);
258        ALOGV("  added %s layer '%s'",
259              (layer.is_global) ? "global" : "instance", props.layerName);
260    }
261
262    return true;
263}
264
265void* LayerLibrary::GetGPA(const Layer& layer,
266                           const char* gpa_name,
267                           size_t gpa_name_len) const {
268    void* gpa;
269    size_t layer_name_len =
270        std::max(size_t{2}, strlen(layer.properties.layerName));
271    char* name = static_cast<char*>(alloca(layer_name_len + gpa_name_len + 1));
272    strcpy(name, layer.properties.layerName);
273    strcpy(name + layer_name_len, gpa_name);
274    if (!(gpa = dlsym(dlhandle_, name))) {
275        strcpy(name, "vk");
276        strcpy(name + 2, gpa_name);
277        gpa = dlsym(dlhandle_, name);
278    }
279    return gpa;
280}
281
282std::vector<LayerLibrary> g_layer_libraries;
283std::vector<Layer> g_instance_layers;
284
285void AddLayerLibrary(const std::string& path) {
286    ALOGV("examining layer library '%s'", path.c_str());
287
288    LayerLibrary library(path);
289    if (!library.Open())
290        return;
291
292    if (!library.EnumerateLayers(g_layer_libraries.size(), g_instance_layers)) {
293        library.Close();
294        return;
295    }
296
297    library.Close();
298
299    g_layer_libraries.emplace_back(std::move(library));
300}
301
302void DiscoverLayersInDirectory(const std::string& dir_path) {
303    ALOGV("looking for layers in '%s'", dir_path.c_str());
304
305    DIR* directory = opendir(dir_path.c_str());
306    if (!directory) {
307        int err = errno;
308        ALOGV_IF(err != ENOENT, "failed to open layer directory '%s': %s (%d)",
309                 dir_path.c_str(), strerror(err), err);
310        return;
311    }
312
313    std::string path;
314    path.reserve(dir_path.size() + 20);
315    path.append(dir_path);
316    path.append("/");
317
318    struct dirent* entry;
319    while ((entry = readdir(directory))) {
320        size_t libname_len = strlen(entry->d_name);
321        if (strncmp(entry->d_name, "libVkLayer", 10) != 0 ||
322            strncmp(entry->d_name + libname_len - 3, ".so", 3) != 0)
323            continue;
324        path.append(entry->d_name);
325        AddLayerLibrary(path);
326        path.resize(dir_path.size() + 1);
327    }
328
329    closedir(directory);
330}
331
332const Layer* FindInstanceLayer(const char* name) {
333    auto layer =
334        std::find_if(g_instance_layers.cbegin(), g_instance_layers.cend(),
335                     [=](const Layer& entry) {
336                         return strcmp(entry.properties.layerName, name) == 0;
337                     });
338    return (layer != g_instance_layers.cend()) ? &*layer : nullptr;
339}
340
341const Layer* FindDeviceLayer(const char* name) {
342    const Layer* layer = FindInstanceLayer(name);
343    return (layer && layer->is_global) ? layer : nullptr;
344}
345
346void* GetLayerGetProcAddr(const Layer& layer,
347                          const char* gpa_name,
348                          size_t gpa_name_len) {
349    const LayerLibrary& library = g_layer_libraries[layer.library_idx];
350    return library.GetGPA(layer, gpa_name, gpa_name_len);
351}
352
353}  // anonymous namespace
354
355void DiscoverLayers() {
356    if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0))
357        DiscoverLayersInDirectory("/data/local/debug/vulkan");
358    if (!LoaderData::GetInstance().layer_path.empty())
359        DiscoverLayersInDirectory(LoaderData::GetInstance().layer_path.c_str());
360}
361
362uint32_t EnumerateInstanceLayers(uint32_t count,
363                                 VkLayerProperties* properties) {
364    uint32_t n =
365        std::min(count, static_cast<uint32_t>(g_instance_layers.size()));
366    for (uint32_t i = 0; i < n; i++)
367        properties[i] = g_instance_layers[i].properties;
368
369    return static_cast<uint32_t>(g_instance_layers.size());
370}
371
372uint32_t EnumerateDeviceLayers(uint32_t count, VkLayerProperties* properties) {
373    uint32_t n = 0;
374    for (const auto& layer : g_instance_layers) {
375        // ignore non-global layers
376        if (!layer.is_global)
377            continue;
378
379        if (n < count)
380            properties[n] = layer.properties;
381        n++;
382    }
383
384    return n;
385}
386
387void GetInstanceLayerExtensions(const char* name,
388                                const VkExtensionProperties** properties,
389                                uint32_t* count) {
390    const Layer* layer = FindInstanceLayer(name);
391    if (layer) {
392        *properties = layer->instance_extensions.data();
393        *count = static_cast<uint32_t>(layer->instance_extensions.size());
394    } else {
395        *properties = nullptr;
396        *count = 0;
397    }
398}
399
400void GetDeviceLayerExtensions(const char* name,
401                              const VkExtensionProperties** properties,
402                              uint32_t* count) {
403    const Layer* layer = FindDeviceLayer(name);
404    if (layer) {
405        *properties = layer->device_extensions.data();
406        *count = static_cast<uint32_t>(layer->device_extensions.size());
407    } else {
408        *properties = nullptr;
409        *count = 0;
410    }
411}
412
413LayerRef GetInstanceLayerRef(const char* name) {
414    const Layer* layer = FindInstanceLayer(name);
415    if (layer) {
416        LayerLibrary& library = g_layer_libraries[layer->library_idx];
417        if (!library.Open())
418            layer = nullptr;
419    }
420
421    return LayerRef(layer, true);
422}
423
424LayerRef GetDeviceLayerRef(const char* name) {
425    const Layer* layer = FindDeviceLayer(name);
426    if (layer) {
427        LayerLibrary& library = g_layer_libraries[layer->library_idx];
428        if (!library.Open())
429            layer = nullptr;
430    }
431
432    return LayerRef(layer, false);
433}
434
435LayerRef::LayerRef(const Layer* layer, bool is_instance)
436    : layer_(layer), is_instance_(is_instance) {}
437
438LayerRef::~LayerRef() {
439    if (layer_) {
440        LayerLibrary& library = g_layer_libraries[layer_->library_idx];
441        library.Close();
442    }
443}
444
445const char* LayerRef::GetName() const {
446    return layer_->properties.layerName;
447}
448
449uint32_t LayerRef::GetSpecVersion() const {
450    return layer_->properties.specVersion;
451}
452
453LayerRef::LayerRef(LayerRef&& other)
454    : layer_(other.layer_), is_instance_(other.is_instance_) {
455    other.layer_ = nullptr;
456}
457
458PFN_vkGetInstanceProcAddr LayerRef::GetGetInstanceProcAddr() const {
459    return layer_ ? reinterpret_cast<PFN_vkGetInstanceProcAddr>(
460                        GetLayerGetProcAddr(*layer_, "GetInstanceProcAddr", 19))
461                  : nullptr;
462}
463
464PFN_vkGetDeviceProcAddr LayerRef::GetGetDeviceProcAddr() const {
465    return layer_ ? reinterpret_cast<PFN_vkGetDeviceProcAddr>(
466                        GetLayerGetProcAddr(*layer_, "GetDeviceProcAddr", 17))
467                  : nullptr;
468}
469
470bool LayerRef::SupportsExtension(const char* name) const {
471    const auto& extensions = (is_instance_) ? layer_->instance_extensions
472                                            : layer_->device_extensions;
473    return std::find_if(extensions.cbegin(), extensions.cend(),
474                        [=](const VkExtensionProperties& ext) {
475                            return strcmp(ext.extensionName, name) == 0;
476                        }) != extensions.cend();
477}
478
479}  // namespace api
480}  // namespace vulkan
481