blob: a44026f96dd2fce7710aaa6a8860ff6de14962bb [file] [log] [blame]
/*
* Copyright 2015 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
// module header
#include "loader.h"
// standard C headers
#include <dirent.h>
#include <dlfcn.h>
#include <inttypes.h>
#include <malloc.h>
#include <pthread.h>
#include <string.h>
// standard C++ headers
#include <algorithm>
#include <mutex>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
// platform/library headers
#include <cutils/properties.h>
#include <hardware/hwvulkan.h>
#include <log/log.h>
#include <vulkan/vk_debug_report_lunarg.h>
#include <vulkan/vulkan_loader_data.h>
using namespace vulkan;
static const uint32_t kMaxPhysicalDevices = 4;
namespace {
// 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);
typedef struct VkLayerLinkedListElem_ {
PFN_vkGetProcAddr get_proc_addr;
void* next_element;
void* base_object;
} VkLayerLinkedListElem;
// Define Handle typedef to be void* as returned from dlopen.
typedef void* SharedLibraryHandle;
// Custom versions of std classes that use the vulkan alloc callback.
template <class T>
class CallbackAllocator {
public:
typedef T value_type;
CallbackAllocator(const VkAllocCallbacks* alloc_input)
: alloc(alloc_input) {}
template <class T2>
CallbackAllocator(const CallbackAllocator<T2>& other)
: alloc(other.alloc) {}
T* allocate(std::size_t n) {
void* mem = alloc->pfnAlloc(alloc->pUserData, n * sizeof(T), alignof(T),
VK_SYSTEM_ALLOC_TYPE_INTERNAL);
return static_cast<T*>(mem);
}
void deallocate(T* array, std::size_t /*n*/) {
alloc->pfnFree(alloc->pUserData, array);
}
const VkAllocCallbacks* alloc;
};
// These are needed in order to move Strings
template <class T>
bool operator==(const CallbackAllocator<T>& alloc1,
const CallbackAllocator<T>& alloc2) {
return alloc1.alloc == alloc2.alloc;
}
template <class T>
bool operator!=(const CallbackAllocator<T>& alloc1,
const CallbackAllocator<T>& alloc2) {
return !(alloc1 == alloc2);
}
template <class Key,
class T,
class Hash = std::hash<Key>,
class Pred = std::equal_to<Key> >
using UnorderedMap =
std::unordered_map<Key,
T,
Hash,
Pred,
CallbackAllocator<std::pair<const Key, T> > >;
template <class T>
using Vector = std::vector<T, CallbackAllocator<T> >;
typedef std::basic_string<char,
std::char_traits<char>,
CallbackAllocator<char> > String;
} // namespace
// -----------------------------------------------------------------------------
namespace {
struct LayerData {
String path;
SharedLibraryHandle handle;
uint32_t ref_count;
};
typedef UnorderedMap<String, LayerData>::iterator LayerMapIterator;
} // namespace
struct VkInstance_T {
VkInstance_T(const VkAllocCallbacks* alloc_callbacks)
: vtbl(&vtbl_storage),
alloc(alloc_callbacks),
num_physical_devices(0),
layers(CallbackAllocator<std::pair<String, LayerData> >(alloc)),
active_layers(CallbackAllocator<String>(alloc)) {
pthread_mutex_init(&layer_lock, 0);
memset(&vtbl_storage, 0, sizeof(vtbl_storage));
memset(physical_devices, 0, sizeof(physical_devices));
memset(&drv.vtbl, 0, sizeof(drv.vtbl));
drv.GetDeviceProcAddr = nullptr;
drv.num_physical_devices = 0;
}
~VkInstance_T() { pthread_mutex_destroy(&layer_lock); }
InstanceVtbl* vtbl;
InstanceVtbl vtbl_storage;
const VkAllocCallbacks* alloc;
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;
VkDbgMsgCallback message;
struct Driver {
// Pointers to driver entry points. Used explicitly by the loader; not
// set as the dispatch table for any objects.
InstanceVtbl vtbl;
// Pointer to the driver's get_device_proc_addr, must be valid for any
// of the driver's physical devices. Not part of the InstanceVtbl since
// it's not an Instance/PhysicalDevice function.
PFN_vkGetDeviceProcAddr GetDeviceProcAddr;
// Number of physical devices owned by this driver.
uint32_t num_physical_devices;
} drv; // may eventually be an array
};
// -----------------------------------------------------------------------------
namespace {
typedef VkInstance_T Instance;
struct Device {
Device(Instance* instance_input)
: instance(instance_input),
active_layers(CallbackAllocator<LayerMapIterator>(instance->alloc)) {
memset(&vtbl_storage, 0, sizeof(vtbl_storage));
vtbl_storage.device = this;
}
DeviceVtbl vtbl_storage;
Instance* instance;
// Vector of layers active for this device
Vector<LayerMapIterator> active_layers;
};
// -----------------------------------------------------------------------------
// Utility Code
inline const InstanceVtbl* GetVtbl(VkPhysicalDevice physicalDevice) {
return *reinterpret_cast<InstanceVtbl**>(physicalDevice);
}
inline const DeviceVtbl* GetVtbl(VkDevice device) {
return *reinterpret_cast<DeviceVtbl**>(device);
}
inline const DeviceVtbl* GetVtbl(VkQueue queue) {
return *reinterpret_cast<DeviceVtbl**>(queue);
}
void* DefaultAlloc(void*, size_t size, size_t alignment, VkSystemAllocType) {
return memalign(alignment, size);
}
void DefaultFree(void*, void* pMem) {
free(pMem);
}
const VkAllocCallbacks kDefaultAllocCallbacks = {
.pUserData = nullptr,
.pfnAlloc = DefaultAlloc,
.pfnFree = DefaultFree,
};
hwvulkan_device_t* g_hwdevice;
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;
}
});
return module != nullptr && g_hwdevice != nullptr;
}
void DestroyDevice(Device* device) {
const VkAllocCallbacks* alloc = device->instance->alloc;
device->~Device();
alloc->pfnFree(alloc->pUserData, device);
}
void FindLayersInDirectory(Instance& instance, const String& dir_name) {
DIR* directory = opendir(dir_name.c_str());
if (!directory) {
android_LogPriority log_priority =
(errno == ENOENT) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_ERROR;
LOG_PRI(log_priority, LOG_TAG,
"failed to open layer directory '%s': %s (%d)",
dir_name.c_str(), strerror(errno), errno);
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;
}
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);
}
struct InstanceNamesPair {
Instance* instance;
Vector<String>* layer_names;
};
void SetLayerNamesFromProperty(const char* name,
const char* value,
void* data) {
const char prefix[] = "debug.vulkan.layer.";
const size_t prefixlen = sizeof(prefix) - 1;
if (value[0] == '\0' || strncmp(name, prefix, prefixlen) != 0)
return;
const char* number_str = name + prefixlen;
long layer_number = strtol(number_str, nullptr, 10);
if (layer_number <= 0 || layer_number == LONG_MAX) {
ALOGW("Cannot use a layer at number %ld from string %s", layer_number,
number_str);
return;
}
auto instance_names_pair = static_cast<InstanceNamesPair*>(data);
Vector<String>* layer_names = instance_names_pair->layer_names;
Instance* instance = instance_names_pair->instance;
size_t layer_size = static_cast<size_t>(layer_number);
if (layer_size > layer_names->size()) {
layer_names->resize(layer_size,
String(CallbackAllocator<char>(instance->alloc)));
}
(*layer_names)[layer_size - 1] = value;
}
template <class TInfo, class TObject>
VkResult ActivateAllLayers(TInfo create_info, Instance* instance, TObject* object) {
ALOG_ASSERT(create_info->sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO ||
create_info->sType == VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
"Cannot activate layers for unknown object %p", object);
CallbackAllocator<char> string_allocator(instance->alloc);
// Load system layers
{
char layer_prop[PROPERTY_VALUE_MAX];
property_get("debug.vulkan.layers", layer_prop, "");
String layer_name(string_allocator);
String layer_prop_str(layer_prop, string_allocator);
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);
start = end + 1;
}
Vector<String> layer_names(CallbackAllocator<String>(instance->alloc));
InstanceNamesPair instance_names_pair = {.instance = instance,
.layer_names = &layer_names};
property_list(SetLayerNamesFromProperty,
static_cast<void*>(&instance_names_pair));
for (auto layer_name_element : layer_names) {
ActivateLayer(object, instance, layer_name_element);
}
}
// Load app layers
for (uint32_t i = 0; i < create_info->layerCount; ++i) {
String layer_name(create_info->ppEnabledLayerNames[i],
string_allocator);
auto element = instance->layers.find(layer_name);
if (element == instance->layers.end()) {
ALOGE("requested %s layer '%s' not present",
create_info->sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO ?
"instance" : "device",
layer_name.c_str());
return VK_ERROR_LAYER_NOT_PRESENT;
} else {
ActivateLayer(object, instance, layer_name);
}
}
return VK_SUCCESS;
}
template <class TCreateInfo>
bool AddExtensionToCreateInfo(TCreateInfo& local_create_info,
const char* extension_name,
const VkAllocCallbacks* alloc) {
for (uint32_t i = 0; i < local_create_info.extensionCount; ++i) {
if (!strcmp(extension_name,
local_create_info.ppEnabledExtensionNames[i])) {
return false;
}
}
uint32_t extension_count = local_create_info.extensionCount;
local_create_info.extensionCount++;
void* mem = alloc->pfnAlloc(
alloc->pUserData, local_create_info.extensionCount * sizeof(char*),
alignof(char*), VK_SYSTEM_ALLOC_TYPE_INTERNAL);
if (mem) {
const char** enabled_extensions = static_cast<const char**>(mem);
for (uint32_t i = 0; i < extension_count; ++i) {
enabled_extensions[i] =
local_create_info.ppEnabledExtensionNames[i];
}
enabled_extensions[extension_count] = extension_name;
local_create_info.ppEnabledExtensionNames = enabled_extensions;
} else {
ALOGW("%s extension cannot be enabled: memory allocation failed",
extension_name);
local_create_info.extensionCount--;
return false;
}
return true;
}
template <class T>
void FreeAllocatedCreateInfo(T& local_create_info,
const VkAllocCallbacks* alloc) {
alloc->pfnFree(
alloc->pUserData,
const_cast<char**>(local_create_info.ppEnabledExtensionNames));
}
VkBool32 LogDebugMessageCallback(VkFlags message_flags,
VkDbgObjectType /*obj_type*/,
uint64_t /*src_object*/,
size_t /*location*/,
int32_t message_code,
const char* layer_prefix,
const char* message,
void* /*user_data*/) {
if (message_flags & VK_DBG_REPORT_ERROR_BIT) {
ALOGE("[%s] Code %d : %s", layer_prefix, message_code, message);
} else if (message_flags & VK_DBG_REPORT_WARN_BIT) {
ALOGW("[%s] Code %d : %s", layer_prefix, message_code, message);
}
return false;
}
VkResult Noop(...) {
return VK_SUCCESS;
}
PFN_vkVoidFunction GetLayerDeviceProcAddr(VkDevice device, const char* name) {
if (strcmp(name, "vkGetDeviceProcAddr") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(GetLayerDeviceProcAddr);
}
if (strcmp(name, "vkCreateDevice") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(Noop);
}
// WSI extensions are not in the driver so return the loader functions
if (strcmp(name, "vkGetSurfacePropertiesKHR") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(GetSurfacePropertiesKHR);
}
if (strcmp(name, "vkGetSurfaceFormatsKHR") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(GetSurfaceFormatsKHR);
}
if (strcmp(name, "vkGetSurfacePresentModesKHR") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(GetSurfacePresentModesKHR);
}
if (strcmp(name, "vkCreateSwapchainKHR") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(CreateSwapchainKHR);
}
if (strcmp(name, "vkDestroySwapchainKHR") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(DestroySwapchainKHR);
}
if (strcmp(name, "vkGetSwapchainImagesKHR") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(GetSwapchainImagesKHR);
}
if (strcmp(name, "vkAcquireNextImageKHR") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(AcquireNextImageKHR);
}
if (strcmp(name, "vkQueuePresentKHR") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(QueuePresentKHR);
}
if (!device)
return GetGlobalDeviceProcAddr(name);
Device* loader_device = reinterpret_cast<Device*>(GetVtbl(device)->device);
return loader_device->instance->drv.GetDeviceProcAddr(device, name);
}
// -----------------------------------------------------------------------------
// "Bottom" functions. These are called at the end of the instance dispatch
// chain.
void DestroyInstanceBottom(VkInstance instance) {
// These checks allow us to call DestroyInstanceBottom from any error path
// in CreateInstanceBottom, before the driver instance is fully initialized.
if (instance->drv.vtbl.instance != VK_NULL_HANDLE &&
instance->drv.vtbl.DestroyInstance) {
instance->drv.vtbl.DestroyInstance(instance->drv.vtbl.instance);
}
if (instance->message) {
PFN_vkDbgDestroyMsgCallback DebugDestroyMessageCallback;
DebugDestroyMessageCallback =
reinterpret_cast<PFN_vkDbgDestroyMsgCallback>(
vkGetInstanceProcAddr(instance, "vkDbgDestroyMsgCallback"));
DebugDestroyMessageCallback(instance, instance->message);
}
for (auto it = instance->active_layers.begin();
it != instance->active_layers.end(); ++it) {
DeactivateLayer(instance, it);
}
const VkAllocCallbacks* alloc = instance->alloc;
instance->~VkInstance_T();
alloc->pfnFree(alloc->pUserData, instance);
}
VkResult CreateInstanceBottom(const VkInstanceCreateInfo* create_info,
VkInstance* instance_ptr) {
Instance* instance = *instance_ptr;
VkResult result;
result =
g_hwdevice->CreateInstance(create_info, &instance->drv.vtbl.instance);
if (result != VK_SUCCESS) {
DestroyInstanceBottom(instance);
return result;
}
if (!LoadInstanceVtbl(
instance->drv.vtbl.instance, instance->drv.vtbl.instance,
g_hwdevice->GetInstanceProcAddr, instance->drv.vtbl)) {
DestroyInstanceBottom(instance);
return VK_ERROR_INITIALIZATION_FAILED;
}
// vkGetDeviceProcAddr has a bootstrapping problem. We require that it be
// queryable from the Instance, and that the resulting function work for any
// VkDevice created from the instance.
instance->drv.GetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(
g_hwdevice->GetInstanceProcAddr(instance->drv.vtbl.instance,
"vkGetDeviceProcAddr"));
if (!instance->drv.GetDeviceProcAddr) {
ALOGE("missing instance proc: \"%s\"", "vkGetDeviceProcAddr");
DestroyInstanceBottom(instance);
return VK_ERROR_INITIALIZATION_FAILED;
}
hwvulkan_dispatch_t* dispatch =
reinterpret_cast<hwvulkan_dispatch_t*>(instance->drv.vtbl.instance);
if (dispatch->magic == HWVULKAN_DISPATCH_MAGIC) {
// Skip setting dispatch->vtbl on the driver instance handle, since we
// never intentionally call through it; we go through Instance::drv.vtbl
// instead.
} else {
ALOGE("invalid VkInstance dispatch magic: 0x%" PRIxPTR,
dispatch->magic);
DestroyInstanceBottom(instance);
return VK_ERROR_INITIALIZATION_FAILED;
}
uint32_t num_physical_devices = 0;
result = instance->drv.vtbl.EnumeratePhysicalDevices(
instance->drv.vtbl.instance, &num_physical_devices, nullptr);
if (result != VK_SUCCESS) {
DestroyInstanceBottom(instance);
return VK_ERROR_INITIALIZATION_FAILED;
}
num_physical_devices = std::min(num_physical_devices, kMaxPhysicalDevices);
result = instance->drv.vtbl.EnumeratePhysicalDevices(
instance->drv.vtbl.instance, &num_physical_devices,
instance->physical_devices);
if (result != VK_SUCCESS) {
DestroyInstanceBottom(instance);
return VK_ERROR_INITIALIZATION_FAILED;
}
for (uint32_t i = 0; i < num_physical_devices; i++) {
dispatch = reinterpret_cast<hwvulkan_dispatch_t*>(
instance->physical_devices[i]);
if (dispatch->magic != HWVULKAN_DISPATCH_MAGIC) {
ALOGE("invalid VkPhysicalDevice dispatch magic: 0x%" PRIxPTR,
dispatch->magic);
DestroyInstanceBottom(instance);
return VK_ERROR_INITIALIZATION_FAILED;
}
dispatch->vtbl = instance->vtbl;
}
instance->drv.num_physical_devices = num_physical_devices;
instance->num_physical_devices = instance->drv.num_physical_devices;
return VK_SUCCESS;
}
VkResult EnumeratePhysicalDevicesBottom(VkInstance instance,
uint32_t* pdev_count,
VkPhysicalDevice* pdevs) {
uint32_t count = instance->num_physical_devices;
if (pdevs) {
count = std::min(count, *pdev_count);
std::copy(instance->physical_devices,
instance->physical_devices + count, pdevs);
}
*pdev_count = count;
return VK_SUCCESS;
}
void GetPhysicalDeviceFeaturesBottom(VkPhysicalDevice pdev,
VkPhysicalDeviceFeatures* features) {
GetVtbl(pdev)->instance->drv.vtbl.GetPhysicalDeviceFeatures(pdev, features);
}
void GetPhysicalDeviceFormatPropertiesBottom(VkPhysicalDevice pdev,
VkFormat format,
VkFormatProperties* properties) {
GetVtbl(pdev)->instance->drv.vtbl.GetPhysicalDeviceFormatProperties(
pdev, format, properties);
}
void GetPhysicalDeviceImageFormatPropertiesBottom(
VkPhysicalDevice pdev,
VkFormat format,
VkImageType type,
VkImageTiling tiling,
VkImageUsageFlags usage,
VkImageCreateFlags flags,
VkImageFormatProperties* properties) {
GetVtbl(pdev)->instance->drv.vtbl.GetPhysicalDeviceImageFormatProperties(
pdev, format, type, tiling, usage, flags, properties);
}
void GetPhysicalDevicePropertiesBottom(VkPhysicalDevice pdev,
VkPhysicalDeviceProperties* properties) {
GetVtbl(pdev)
->instance->drv.vtbl.GetPhysicalDeviceProperties(pdev, properties);
}
void GetPhysicalDeviceQueueFamilyPropertiesBottom(
VkPhysicalDevice pdev,
uint32_t* pCount,
VkQueueFamilyProperties* properties) {
GetVtbl(pdev)->instance->drv.vtbl.GetPhysicalDeviceQueueFamilyProperties(
pdev, pCount, properties);
}
void GetPhysicalDeviceMemoryPropertiesBottom(
VkPhysicalDevice pdev,
VkPhysicalDeviceMemoryProperties* properties) {
GetVtbl(pdev)->instance->drv.vtbl.GetPhysicalDeviceMemoryProperties(
pdev, properties);
}
VkResult CreateDeviceBottom(VkPhysicalDevice pdev,
const VkDeviceCreateInfo* create_info,
VkDevice* out_device) {
Instance& instance = *static_cast<Instance*>(GetVtbl(pdev)->instance);
VkResult result;
void* mem = instance.alloc->pfnAlloc(instance.alloc->pUserData,
sizeof(Device), alignof(Device),
VK_SYSTEM_ALLOC_TYPE_API_OBJECT);
if (!mem)
return VK_ERROR_OUT_OF_HOST_MEMORY;
Device* device = new (mem) Device(&instance);
result = ActivateAllLayers(create_info, &instance, device);
if (result != VK_SUCCESS) {
DestroyDevice(device);
return result;
}
VkDevice drv_device;
result = instance.drv.vtbl.CreateDevice(pdev, create_info, &drv_device);
if (result != VK_SUCCESS) {
DestroyDevice(device);
return result;
}
hwvulkan_dispatch_t* dispatch =
reinterpret_cast<hwvulkan_dispatch_t*>(drv_device);
if (dispatch->magic != HWVULKAN_DISPATCH_MAGIC) {
ALOGE("invalid VkDevice dispatch magic: 0x%" PRIxPTR, dispatch->magic);
PFN_vkDestroyDevice destroy_device =
reinterpret_cast<PFN_vkDestroyDevice>(
instance.drv.GetDeviceProcAddr(drv_device, "vkDestroyDevice"));
destroy_device(drv_device);
DestroyDevice(device);
return VK_ERROR_INITIALIZATION_FAILED;
}
dispatch->vtbl = &device->vtbl_storage;
void* base_object = static_cast<void*>(drv_device);
void* next_object = base_object;
VkLayerLinkedListElem* next_element;
PFN_vkGetDeviceProcAddr next_get_proc_addr = GetLayerDeviceProcAddr;
Vector<VkLayerLinkedListElem> elem_list(
device->active_layers.size(),
CallbackAllocator<VkLayerLinkedListElem>(instance.alloc));
for (size_t i = elem_list.size(); i > 0; i--) {
size_t idx = i - 1;
next_element = &elem_list[idx];
next_element->get_proc_addr =
reinterpret_cast<PFN_vkGetProcAddr>(next_get_proc_addr);
next_element->base_object = base_object;
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()));
if (!next_get_proc_addr) {
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);
}
}
}
if (!LoadDeviceVtbl(static_cast<VkDevice>(base_object),
static_cast<VkDevice>(next_object), next_get_proc_addr,
device->vtbl_storage)) {
DestroyDevice(device);
return VK_ERROR_INITIALIZATION_FAILED;
}
PFN_vkCreateDevice layer_createDevice =
reinterpret_cast<PFN_vkCreateDevice>(
device->vtbl_storage.GetDeviceProcAddr(drv_device,
"vkCreateDevice"));
layer_createDevice(pdev, create_info, &drv_device);
// TODO(mlentine) : This is needed to use WSI layer validation. Remove this
// when new version of layer initialization exits.
if (!LoadDeviceVtbl(static_cast<VkDevice>(base_object),
static_cast<VkDevice>(next_object), next_get_proc_addr,
device->vtbl_storage)) {
DestroyDevice(device);
return VK_ERROR_INITIALIZATION_FAILED;
}
*out_device = drv_device;
return VK_SUCCESS;
}
VkResult EnumerateDeviceExtensionPropertiesBottom(
VkPhysicalDevice pdev,
const char* layer_name,
uint32_t* properties_count,
VkExtensionProperties* properties) {
// TODO: what are we supposed to do with layer_name here?
return GetVtbl(pdev)->instance->drv.vtbl.EnumerateDeviceExtensionProperties(
pdev, layer_name, properties_count, properties);
}
VkResult EnumerateDeviceLayerPropertiesBottom(VkPhysicalDevice pdev,
uint32_t* properties_count,
VkLayerProperties* properties) {
return GetVtbl(pdev)->instance->drv.vtbl.EnumerateDeviceLayerProperties(
pdev, properties_count, properties);
}
void GetPhysicalDeviceSparseImageFormatPropertiesBottom(
VkPhysicalDevice pdev,
VkFormat format,
VkImageType type,
uint32_t samples,
VkImageUsageFlags usage,
VkImageTiling tiling,
uint32_t* properties_count,
VkSparseImageFormatProperties* properties) {
GetVtbl(pdev)
->instance->drv.vtbl.GetPhysicalDeviceSparseImageFormatProperties(
pdev, format, type, samples, usage, tiling, properties_count,
properties);
}
PFN_vkVoidFunction GetInstanceProcAddrBottom(VkInstance, const char*);
const InstanceVtbl kBottomInstanceFunctions = {
// clang-format off
.instance = nullptr,
.CreateInstance = CreateInstanceBottom,
.DestroyInstance = DestroyInstanceBottom,
.GetInstanceProcAddr = GetInstanceProcAddrBottom,
.EnumeratePhysicalDevices = EnumeratePhysicalDevicesBottom,
.GetPhysicalDeviceFeatures = GetPhysicalDeviceFeaturesBottom,
.GetPhysicalDeviceFormatProperties = GetPhysicalDeviceFormatPropertiesBottom,
.GetPhysicalDeviceImageFormatProperties = GetPhysicalDeviceImageFormatPropertiesBottom,
.GetPhysicalDeviceProperties = GetPhysicalDevicePropertiesBottom,
.GetPhysicalDeviceQueueFamilyProperties = GetPhysicalDeviceQueueFamilyPropertiesBottom,
.GetPhysicalDeviceMemoryProperties = GetPhysicalDeviceMemoryPropertiesBottom,
.CreateDevice = CreateDeviceBottom,
.EnumerateDeviceExtensionProperties = EnumerateDeviceExtensionPropertiesBottom,
.EnumerateDeviceLayerProperties = EnumerateDeviceLayerPropertiesBottom,
.GetPhysicalDeviceSparseImageFormatProperties = GetPhysicalDeviceSparseImageFormatPropertiesBottom,
.GetPhysicalDeviceSurfaceSupportKHR = GetPhysicalDeviceSurfaceSupportKHR,
// clang-format on
};
PFN_vkVoidFunction GetInstanceProcAddrBottom(VkInstance, const char* name) {
// TODO: Possibly move this into the instance table
// TODO: Possibly register the callbacks in the loader
if (strcmp(name, "vkDbgCreateMsgCallback") == 0 ||
strcmp(name, "vkDbgDestroyMsgCallback") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(Noop);
}
if (strcmp(name, "vkCreateInstance") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(CreateInstanceBottom);
}
return GetSpecificInstanceProcAddr(&kBottomInstanceFunctions, name);
}
} // namespace
// -----------------------------------------------------------------------------
// Global functions. These are called directly from the loader entry points,
// without going through a dispatch table.
namespace vulkan {
VkResult EnumerateInstanceExtensionProperties(
const char* /*layer_name*/,
uint32_t* count,
VkExtensionProperties* /*properties*/) {
if (!EnsureInitialized())
return VK_ERROR_INITIALIZATION_FAILED;
// TODO: not yet implemented
ALOGW("vkEnumerateInstanceExtensionProperties not implemented");
*count = 0;
return VK_SUCCESS;
}
VkResult EnumerateInstanceLayerProperties(uint32_t* count,
VkLayerProperties* /*properties*/) {
if (!EnsureInitialized())
return VK_ERROR_INITIALIZATION_FAILED;
// TODO: not yet implemented
ALOGW("vkEnumerateInstanceLayerProperties not implemented");
*count = 0;
return VK_SUCCESS;
}
VkResult CreateInstance(const VkInstanceCreateInfo* create_info,
VkInstance* out_instance) {
VkResult result;
if (!EnsureInitialized())
return VK_ERROR_INITIALIZATION_FAILED;
VkInstanceCreateInfo local_create_info = *create_info;
if (!local_create_info.pAllocCb)
local_create_info.pAllocCb = &kDefaultAllocCallbacks;
create_info = &local_create_info;
void* instance_mem = create_info->pAllocCb->pfnAlloc(
create_info->pAllocCb->pUserData, sizeof(Instance), alignof(Instance),
VK_SYSTEM_ALLOC_TYPE_API_OBJECT);
if (!instance_mem)
return VK_ERROR_OUT_OF_HOST_MEMORY;
Instance* instance = new (instance_mem) Instance(create_info->pAllocCb);
instance->vtbl_storage = kBottomInstanceFunctions;
instance->vtbl_storage.instance = instance;
instance->message = VK_NULL_HANDLE;
// Scan layers
CallbackAllocator<char> string_allocator(instance->alloc);
String dir_name("/data/local/tmp/vulkan/", string_allocator);
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) {
DestroyInstanceBottom(instance);
return result;
}
void* base_object = static_cast<void*>(instance);
void* next_object = base_object;
VkLayerLinkedListElem* next_element;
PFN_vkGetInstanceProcAddr next_get_proc_addr =
kBottomInstanceFunctions.GetInstanceProcAddr;
Vector<VkLayerLinkedListElem> elem_list(
instance->active_layers.size(),
CallbackAllocator<VkLayerLinkedListElem>(instance->alloc));
for (size_t i = elem_list.size(); i > 0; i--) {
size_t idx = i - 1;
next_element = &elem_list[idx];
next_element->get_proc_addr =
reinterpret_cast<PFN_vkGetProcAddr>(next_get_proc_addr);
next_element->base_object = base_object;
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()));
if (!next_get_proc_addr) {
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);
}
}
}
if (!LoadInstanceVtbl(static_cast<VkInstance>(base_object),
static_cast<VkInstance>(next_object),
next_get_proc_addr, instance->vtbl_storage)) {
DestroyInstanceBottom(instance);
return VK_ERROR_INITIALIZATION_FAILED;
}
// Force enable callback extension if required
bool enable_callback =
property_get_bool("debug.vulkan.enable_callback", false);
bool enable_logging = enable_callback;
const char* extension_name = "DEBUG_REPORT";
if (enable_callback) {
enable_callback = AddExtensionToCreateInfo(
local_create_info, extension_name, instance->alloc);
}
*out_instance = instance;
result = instance->vtbl_storage.CreateInstance(create_info, out_instance);
if (enable_callback)
FreeAllocatedCreateInfo(local_create_info, instance->alloc);
if (result <= 0) {
// For every layer, including the loader top and bottom layers:
// - If a call to the next CreateInstance fails, the layer must clean
// up anything it has successfully done so far, and propagate the
// error upwards.
// - If a layer successfully calls the next layer's CreateInstance, and
// afterwards must fail for some reason, it must call the next layer's
// DestroyInstance before returning.
// - The layer must not call the next layer's DestroyInstance if that
// layer's CreateInstance wasn't called, or returned failure.
// On failure, CreateInstanceBottom frees the instance struct, so it's
// already gone at this point. Nothing to do.
}
if (enable_logging) {
PFN_vkDbgCreateMsgCallback DebugCreateMessageCallback;
DebugCreateMessageCallback =
reinterpret_cast<PFN_vkDbgCreateMsgCallback>(
vkGetInstanceProcAddr(instance, "vkDbgCreateMsgCallback"));
DebugCreateMessageCallback(
instance, VK_DBG_REPORT_ERROR_BIT | VK_DBG_REPORT_WARN_BIT,
LogDebugMessageCallback, NULL, &instance->message);
}
return result;
}
PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* name) {
if (!instance)
return GetGlobalInstanceProcAddr(name);
// TODO: Possibly move this into the instance table
if (strcmp(name, "vkDbgCreateMsgCallback") == 0 ||
strcmp(name, "vkDbgDestroyMsgCallback") == 0) {
if (!instance->vtbl)
return NULL;
PFN_vkGetInstanceProcAddr gpa = instance->vtbl->GetInstanceProcAddr;
return reinterpret_cast<PFN_vkVoidFunction>(gpa(instance, name));
}
// For special-case functions we always return the loader entry
if (strcmp(name, "vkGetInstanceProcAddr") == 0 ||
strcmp(name, "vkGetDeviceProcAddr") == 0) {
return GetGlobalInstanceProcAddr(name);
}
return GetSpecificInstanceProcAddr(instance->vtbl, name);
}
PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* name) {
if (!device)
return GetGlobalDeviceProcAddr(name);
if (strcmp(name, "vkGetDeviceProcAddr") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(GetDeviceProcAddr);
}
if (strcmp(name, "vkGetDeviceQueue") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(GetDeviceQueue);
}
if (strcmp(name, "vkAllocCommandBuffers") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(AllocCommandBuffers);
}
if (strcmp(name, "vkDestroyDevice") == 0) {
return reinterpret_cast<PFN_vkVoidFunction>(DestroyDevice);
}
return GetSpecificDeviceProcAddr(GetVtbl(device), name);
}
void GetDeviceQueue(VkDevice drv_device,
uint32_t family,
uint32_t index,
VkQueue* out_queue) {
VkResult result;
VkQueue queue;
const DeviceVtbl* vtbl = GetVtbl(drv_device);
vtbl->GetDeviceQueue(drv_device, family, index, &queue);
hwvulkan_dispatch_t* dispatch =
reinterpret_cast<hwvulkan_dispatch_t*>(queue);
if (dispatch->magic != HWVULKAN_DISPATCH_MAGIC && dispatch->vtbl != vtbl)
ALOGE("invalid VkQueue dispatch magic: 0x%" PRIxPTR, dispatch->magic);
dispatch->vtbl = vtbl;
*out_queue = queue;
}
VkResult AllocCommandBuffers(VkDevice device,
const VkCmdBufferAllocInfo* alloc_info,
VkCmdBuffer* cmdbuffers) {
const DeviceVtbl* vtbl = GetVtbl(device);
VkResult result = vtbl->AllocCommandBuffers(device, alloc_info, cmdbuffers);
if (result != VK_SUCCESS)
return result;
for (uint32_t i = 0; i < alloc_info->count; i++) {
hwvulkan_dispatch_t* dispatch =
reinterpret_cast<hwvulkan_dispatch_t*>(cmdbuffers[i]);
ALOGE_IF(dispatch->magic != HWVULKAN_DISPATCH_MAGIC,
"invalid VkCmdBuffer dispatch magic: 0x%" PRIxPTR,
dispatch->magic);
dispatch->vtbl = vtbl;
}
return VK_SUCCESS;
}
VkResult DestroyDevice(VkDevice drv_device) {
const DeviceVtbl* vtbl = GetVtbl(drv_device);
Device* device = static_cast<Device*>(vtbl->device);
for (auto it = device->active_layers.begin();
it != device->active_layers.end(); ++it) {
DeactivateLayer(device->instance, it);
}
vtbl->DestroyDevice(drv_device);
DestroyDevice(device);
return VK_SUCCESS;
}
void* AllocMem(VkInstance instance,
size_t size,
size_t align,
VkSystemAllocType type) {
const VkAllocCallbacks* alloc_cb = instance->alloc;
return alloc_cb->pfnAlloc(alloc_cb->pUserData, size, align, type);
}
void FreeMem(VkInstance instance, void* ptr) {
const VkAllocCallbacks* alloc_cb = instance->alloc;
alloc_cb->pfnFree(alloc_cb->pUserData, ptr);
}
void* AllocMem(VkDevice device,
size_t size,
size_t align,
VkSystemAllocType type) {
const VkAllocCallbacks* alloc_cb =
static_cast<Device*>(GetVtbl(device)->device)->instance->alloc;
return alloc_cb->pfnAlloc(alloc_cb->pUserData, size, align, type);
}
void FreeMem(VkDevice device, void* ptr) {
const VkAllocCallbacks* alloc_cb =
static_cast<Device*>(GetVtbl(device)->device)->instance->alloc;
alloc_cb->pfnFree(alloc_cb->pUserData, ptr);
}
const DeviceVtbl& GetDriverVtbl(VkDevice device) {
// TODO(jessehall): This actually returns the API-level vtbl for the
// device, not the driver entry points. Given the current use -- getting
// the driver's private swapchain-related functions -- that works, but is
// misleading and likely to cause bugs. Fix as part of separating the
// loader->driver interface from the app->loader interface.
return static_cast<Device*>(GetVtbl(device)->device)->vtbl_storage;
}
const DeviceVtbl& GetDriverVtbl(VkQueue queue) {
// TODO(jessehall): This actually returns the API-level vtbl for the
// device, not the driver entry points. Given the current use -- getting
// the driver's private swapchain-related functions -- that works, but is
// misleading and likely to cause bugs. Fix as part of separating the
// loader->driver interface from the app->loader interface.
return static_cast<Device*>(GetVtbl(queue)->device)->vtbl_storage;
}
} // namespace vulkan