blob: 01750468b43ef699c53e2fdda80875016bb6d0cd [file] [log] [blame]
/*
* Vulkan
*
* Copyright (C) 2014 LunarG, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Authors:
* Chia-I Wu <olv@lunarg.com>
* Jon Ashburn <jon@lunarg.com>
* Courtney Goeltzenleuchter <courtney@lunarg.com>
* Ian Elliott <ian@lunarg.com>
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
#if defined(WIN32)
#include "dirent_on_windows.h"
#else // WIN32
#include <dirent.h>
#endif // WIN32
#include "loader_platform.h"
#include "loader.h"
#include "wsi_lunarg.h"
#include "gpa_helper.h"
#include "table_ops.h"
#include "debug_report.h"
#include "vkIcd.h"
// The following is #included again to catch certain OS-specific functions
// being used:
#include "loader_platform.h"
void loader_add_to_ext_list(
struct loader_extension_list *ext_list,
uint32_t prop_list_count,
const struct loader_extension_property *prop_list);
static loader_platform_dl_handle loader_add_layer_lib(
const char *chain_type,
struct loader_extension_property *ext_prop);
static void loader_remove_layer_lib(
struct loader_instance *inst,
struct loader_extension_property *ext_prop);
struct loader_struct loader = {0};
static void * VKAPI loader_GetInstanceProcAddr(VkInstance instance, const char * pName);
enum loader_debug {
LOADER_INFO_BIT = VK_BIT(0),
LOADER_WARN_BIT = VK_BIT(1),
LOADER_PERF_BIT = VK_BIT(2),
LOADER_ERROR_BIT = VK_BIT(3),
LOADER_DEBUG_BIT = VK_BIT(4),
};
uint32_t g_loader_debug = 0;
uint32_t g_loader_log_msgs = 0;
//thread safety lock for accessing global data structures such as "loader"
// all entrypoints on the instance chain need to be locked except GPA
// additionally DestroyDevice needs to be locked
loader_platform_thread_mutex loader_lock;
const VkLayerInstanceDispatchTable instance_disp = {
.GetInstanceProcAddr = loader_GetInstanceProcAddr,
.CreateInstance = loader_CreateInstance,
.DestroyInstance = loader_DestroyInstance,
.EnumeratePhysicalDevices = loader_EnumeratePhysicalDevices,
.GetPhysicalDeviceFeatures = loader_GetPhysicalDeviceFeatures,
.GetPhysicalDeviceFormatInfo = loader_GetPhysicalDeviceFormatInfo,
.GetPhysicalDeviceLimits = loader_GetPhysicalDeviceLimits,
.CreateDevice = loader_CreateDevice,
.GetPhysicalDeviceProperties = loader_GetPhysicalDeviceProperties,
.GetPhysicalDevicePerformance = loader_GetPhysicalDevicePerformance,
.GetPhysicalDeviceQueueCount = loader_GetPhysicalDeviceQueueCount,
.GetPhysicalDeviceQueueProperties = loader_GetPhysicalDeviceQueueProperties,
.GetPhysicalDeviceMemoryProperties = loader_GetPhysicalDeviceMemoryProperties,
.GetPhysicalDeviceExtensionCount = loader_GetPhysicalDeviceExtensionCount,
.GetPhysicalDeviceExtensionProperties = loader_GetPhysicalDeviceExtensionProperties,
.DbgCreateMsgCallback = loader_DbgCreateMsgCallback,
.DbgDestroyMsgCallback = loader_DbgDestroyMsgCallback,
};
LOADER_PLATFORM_THREAD_ONCE_DECLARATION(once_icd);
LOADER_PLATFORM_THREAD_ONCE_DECLARATION(once_layer);
LOADER_PLATFORM_THREAD_ONCE_DECLARATION(once_exts);
#if defined(WIN32)
char *loader_get_registry_string(const HKEY hive,
const LPCTSTR sub_key,
const char *value)
{
DWORD access_flags = KEY_QUERY_VALUE;
DWORD value_type;
HKEY key;
VkResult rtn_value;
char *rtn_str = NULL;
DWORD rtn_len = 0;
size_t allocated_len = 0;
rtn_value = RegOpenKeyEx(hive, sub_key, 0, access_flags, &key);
if (rtn_value != ERROR_SUCCESS) {
// We didn't find the key. Try the 32-bit hive (where we've seen the
// key end up on some people's systems):
access_flags |= KEY_WOW64_32KEY;
rtn_value = RegOpenKeyEx(hive, sub_key, 0, access_flags, &key);
if (rtn_value != ERROR_SUCCESS) {
// We still couldn't find the key, so give up:
return NULL;
}
}
rtn_value = RegQueryValueEx(key, value, NULL, &value_type,
(PVOID) rtn_str, (LPDWORD) &rtn_len);
if (rtn_value == ERROR_SUCCESS) {
// If we get to here, we found the key, and need to allocate memory
// large enough for rtn_str, and query again:
allocated_len = rtn_len + 4;
rtn_str = malloc(allocated_len);
rtn_value = RegQueryValueEx(key, value, NULL, &value_type,
(PVOID) rtn_str, (LPDWORD) &rtn_len);
if (rtn_value == ERROR_SUCCESS) {
// We added 4 extra bytes to rtn_str, so that we can ensure that
// the string is NULL-terminated (albeit, in a brute-force manner):
rtn_str[allocated_len-1] = '\0';
} else {
// This should never occur, but in case it does, clean up:
free(rtn_str);
rtn_str = NULL;
}
} // else - shouldn't happen, but if it does, return rtn_str, which is NULL
// Close the registry key that was opened:
RegCloseKey(key);
return rtn_str;
}
// For ICD developers, look in the registry, and look for an environment
// variable for a path(s) where to find the ICD(s):
static char *loader_get_registry_and_env(const char *env_var,
const char *registry_value)
{
char *env_str = getenv(env_var);
size_t env_len = (env_str == NULL) ? 0 : strlen(env_str);
char *registry_str = NULL;
size_t registry_len = 0;
char *rtn_str = NULL;
size_t rtn_len;
registry_str = loader_get_registry_string(HKEY_LOCAL_MACHINE,
"Software\\Vulkan",
registry_value);
registry_len = (registry_str) ? (DWORD) strlen(registry_str) : 0;
rtn_len = env_len + registry_len + 1;
if (rtn_len <= 2) {
// We found neither the desired registry value, nor the environment
// variable; return NULL:
return NULL;
} else {
// We found something, and so we need to allocate memory for the string
// to return:
rtn_str = malloc(rtn_len);
}
if (registry_len == 0) {
// We didn't find the desired registry value, and so we must have found
// only the environment variable:
_snprintf(rtn_str, rtn_len, "%s", env_str);
} else if (env_str != NULL) {
// We found both the desired registry value and the environment
// variable, so concatenate them both:
_snprintf(rtn_str, rtn_len, "%s;%s", registry_str, env_str);
} else {
// We must have only found the desired registry value:
_snprintf(rtn_str, rtn_len, "%s", registry_str);
}
if (registry_str) {
free(registry_str);
}
return(rtn_str);
}
#endif // WIN32
static void loader_log(VkFlags msg_type, int32_t msg_code,
const char *format, ...)
{
char msg[256];
va_list ap;
int ret;
if (!(msg_type & g_loader_log_msgs)) {
return;
}
va_start(ap, format);
ret = vsnprintf(msg, sizeof(msg), format, ap);
if ((ret >= (int) sizeof(msg)) || ret < 0) {
msg[sizeof(msg) - 1] = '\0';
}
va_end(ap);
#if defined(WIN32)
OutputDebugString(msg);
#endif
fputs(msg, stderr);
fputc('\n', stderr);
}
bool compare_vk_extension_properties(const VkExtensionProperties *op1, const VkExtensionProperties *op2)
{
return memcmp(op1, op2, sizeof(VkExtensionProperties)) == 0 ? true : false;
}
/*
* Search the given ext_list for an extension
* matching the given vk_ext_prop
*/
bool has_vk_extension_property(
const VkExtensionProperties *vk_ext_prop,
const struct loader_extension_list *ext_list)
{
for (uint32_t i = 0; i < ext_list->count; i++) {
if (compare_vk_extension_properties(&ext_list->list[i].info, vk_ext_prop))
return true;
}
return false;
}
/*
* Search the given ext_list for an extension
* matching the given vk_ext_prop
*/
static struct loader_extension_property *get_extension_property_from_vkext(
const VkExtensionProperties *vk_ext_prop,
const struct loader_extension_list *ext_list)
{
for (uint32_t i = 0; i < ext_list->count; i++) {
if (compare_vk_extension_properties(&ext_list->list[i].info, vk_ext_prop))
return &ext_list->list[i];
}
return NULL;
}
static void loader_get_global_extensions(
const PFN_vkGetGlobalExtensionCount fp_get_count,
const PFN_vkGetGlobalExtensionProperties fp_get_props,
const PFN_vkGPA get_proc_addr,
const char *lib_name,
const loader_platform_dl_handle lib_handle,
const enum extension_origin origin,
struct loader_extension_list *ext_list)
{
uint32_t i, count;
struct loader_extension_property ext_props;
VkResult res;
PFN_vkGPA ext_get_proc_addr;
PFN_vkGetInstanceProcAddr get_instance_proc_addr;
res = fp_get_count(&count);
if (res != VK_SUCCESS) {
loader_log(VK_DBG_REPORT_WARN_BIT, 0, "Error getting global extension count from ICD");
return;
}
if (get_proc_addr == NULL)
get_instance_proc_addr = (PFN_vkGetInstanceProcAddr) loader_platform_get_proc_address(lib_handle, "vkGetInstanceProcAddr");
for (i = 0; i < count; i++) {
memset(&ext_props, 0, sizeof(ext_props));
res = fp_get_props(i, &ext_props.info);
if (res == VK_SUCCESS) {
//TODO eventually get this from the layer config file
if (get_proc_addr == NULL) {
char funcStr[MAX_EXTENSION_NAME_SIZE+1];
snprintf(funcStr, MAX_EXTENSION_NAME_SIZE, "%sGetInstanceProcAddr", ext_props.info.name);
if ((ext_get_proc_addr = (PFN_vkGPA) loader_platform_get_proc_address(lib_handle, funcStr)) == NULL)
ext_get_proc_addr = get_instance_proc_addr;
}
ext_props.origin = origin;
ext_props.lib_name = lib_name;
ext_props.get_proc_addr = (get_proc_addr == NULL) ? ext_get_proc_addr : get_proc_addr;
loader_add_to_ext_list(ext_list, 1, &ext_props);
}
}
return;
}
static void loader_get_physical_device_layer_extensions(
struct loader_instance *ptr_instance,
VkPhysicalDevice physical_device,
const uint32_t layer_index,
struct loader_extension_list *ext_list)
{
uint32_t i, count;
VkResult res;
loader_platform_dl_handle lib_handle;
PFN_vkGetPhysicalDeviceExtensionProperties get_phys_dev_ext_props;
PFN_vkGetPhysicalDeviceExtensionCount get_phys_dev_ext_count;
struct loader_extension_property ext_props;
PFN_vkGPA ext_get_proc_addr;
PFN_vkGetDeviceProcAddr get_device_proc_addr;
if (!loader.scanned_layers[layer_index].physical_device_extensions_supported) {
return;
}
ext_props.origin = VK_EXTENSION_ORIGIN_LAYER;
ext_props.lib_name = loader.scanned_layers[layer_index].lib_name;
char funcStr[MAX_EXTENSION_NAME_SIZE+1]; // add one character for 0 termination
lib_handle = loader_add_layer_lib("device", &ext_props);
get_phys_dev_ext_props = (PFN_vkGetPhysicalDeviceExtensionProperties) loader_platform_get_proc_address(lib_handle, "vkGetPhysicalDeviceExtensionProperties");
get_phys_dev_ext_count = (PFN_vkGetPhysicalDeviceExtensionCount) loader_platform_get_proc_address(lib_handle, "vkGetPhysicalDeviceExtensionCount");
get_device_proc_addr = (PFN_vkGetDeviceProcAddr) loader_platform_get_proc_address(lib_handle, "vkGetDeviceProcAddr");
if (get_phys_dev_ext_count) {
res = get_phys_dev_ext_count(physical_device, &count);
if (res == VK_SUCCESS) {
for (i = 0; i < count; i++) {
memset(&ext_props, 0, sizeof(ext_props));
res = get_phys_dev_ext_props(physical_device, i, &ext_props.info);
if (res == VK_SUCCESS && (ext_props.info.sType == VK_STRUCTURE_TYPE_EXTENSION_PROPERTIES)) {
ext_props.origin = VK_EXTENSION_ORIGIN_LAYER;
//TODO eventually get this from the layer config file
snprintf(funcStr, MAX_EXTENSION_NAME_SIZE, "%sGetDeviceProcAddr", ext_props.info.name);
if ((ext_get_proc_addr = (PFN_vkGPA) loader_platform_get_proc_address(lib_handle, funcStr)) == NULL)
ext_get_proc_addr = get_device_proc_addr;
ext_props.get_proc_addr = ext_get_proc_addr;
ext_props.lib_name = loader.scanned_layers[layer_index].lib_name;
loader_add_to_ext_list(ext_list, 1, &ext_props);
}
}
} else {
loader_log(VK_DBG_REPORT_WARN_BIT, 0, "Error getting physical device extension info count from Layer %s", ext_props.lib_name);
}
}
loader_remove_layer_lib(ptr_instance, &ext_props);
return;
}
static bool loader_init_ext_list(struct loader_extension_list *ext_info)
{
ext_info->capacity = 32 * sizeof(struct loader_extension_property);
ext_info->list = malloc(ext_info->capacity);
if (ext_info->list == NULL) {
return false;
}
memset(ext_info->list, 0, ext_info->capacity);
ext_info->count = 0;
return true;
}
void loader_destroy_ext_list(struct loader_extension_list *ext_info)
{
free(ext_info->list);
ext_info->count = 0;
ext_info->capacity = 0;
}
static void loader_add_vk_ext_to_ext_list(
struct loader_extension_list *ext_list,
uint32_t prop_list_count,
const VkExtensionProperties *props,
const struct loader_extension_list *search_list)
{
struct loader_extension_property *ext_prop;
for (uint32_t i = 0; i < prop_list_count; i++) {
// look for duplicates
if (has_vk_extension_property(&props[i], ext_list)) {
continue;
}
ext_prop = get_extension_property_from_vkext(&props[i], search_list);
if (!ext_prop) {
loader_log(VK_DBG_REPORT_WARN_BIT, 0, "Unable to find extension %s", props[i].name);
continue;
}
loader_add_to_ext_list(ext_list, 1, ext_prop);
}
}
/*
* Append non-duplicate extension properties defined in prop_list
* to the given ext_info list
*/
void loader_add_to_ext_list(
struct loader_extension_list *ext_list,
uint32_t prop_list_count,
const struct loader_extension_property *props)
{
uint32_t i;
struct loader_extension_property *cur_ext;
if (ext_list->list == NULL || ext_list->capacity == 0) {
loader_init_ext_list(ext_list);
}
if (ext_list->list == NULL)
return;
for (i = 0; i < prop_list_count; i++) {
cur_ext = (struct loader_extension_property *) &props[i];
// look for duplicates
if (has_vk_extension_property(&cur_ext->info, ext_list)) {
continue;
}
// add to list at end
// check for enough capacity
if (ext_list->count * sizeof(struct loader_extension_property)
>= ext_list->capacity) {
// double capacity
ext_list->capacity *= 2;
ext_list->list = realloc(ext_list->list, ext_list->capacity);
}
/*
* Check if any extensions already on the list come from the same
* library and use the same Get*ProcAddr. If so, link this
* extension to the previous as an alias. That way when we activate
* extensions we only activiate the associated layer once no
* matter how many extensions are used.
*/
for (uint32_t j = 0; j < ext_list->count; j++) {
struct loader_extension_property *active_property = &ext_list->list[j];
if (cur_ext->lib_name &&
cur_ext->origin == VK_EXTENSION_ORIGIN_LAYER &&
active_property->origin == VK_EXTENSION_ORIGIN_LAYER &&
strcmp(cur_ext->lib_name, active_property->lib_name) == 0 &&
(cur_ext->get_proc_addr == active_property->get_proc_addr) &&
active_property->alias == NULL) {
cur_ext->alias = active_property;
break;
}
}
memcpy(&ext_list->list[ext_list->count], cur_ext, sizeof(struct loader_extension_property));
ext_list->count++;
}
}
/*
* Search the search_list for any extension with
* a name that matches the given ext_name.
* Add all matching extensions to the found_list
* Do not add if found VkExtensionProperties is already
* on the found_list
*/
static void loader_search_ext_list_for_name(
const char *ext_name,
const struct loader_extension_list *search_list,
struct loader_extension_list *found_list)
{
for (uint32_t i = 0; i < search_list->count; i++) {
struct loader_extension_property *ext_prop = &search_list->list[i];
if (ext_prop->origin == VK_EXTENSION_ORIGIN_LAYER &&
0 == strcmp(ext_prop->info.name, ext_name)) {
/* Found an extension with the same name, add to found_list */
loader_add_to_ext_list(found_list, 1, &search_list->list[i]);
}
}
}
bool loader_is_extension_scanned(const VkExtensionProperties *ext_prop)
{
uint32_t i;
for (i = 0; i < loader.global_extensions.count; i++) {
if (compare_vk_extension_properties(&loader.global_extensions.list[i].info, ext_prop))
return true;
}
return false;
}
void loader_coalesce_extensions(void)
{
uint32_t i;
struct loader_scanned_icds *icd_list = loader.scanned_icd_list;
// traverse scanned icd list adding non-duplicate extensions to the list
while (icd_list != NULL) {
loader_add_to_ext_list(&loader.global_extensions,
icd_list->global_extension_list.count,
icd_list->global_extension_list.list);
icd_list = icd_list->next;
};
//Traverse layers list adding non-duplicate extensions to the list
for (i = 0; i < loader.scanned_layer_count; i++) {
loader_add_to_ext_list(&loader.global_extensions,
loader.scanned_layers[i].global_extension_list.count,
loader.scanned_layers[i].global_extension_list.list);
}
// Traverse loader's extensions, adding non-duplicate extensions to the list
debug_report_add_instance_extensions(&loader.global_extensions);
}
static struct loader_icd *loader_get_icd_and_device(const VkDevice device,
struct loader_device **found_dev)
{
*found_dev = NULL;
for (struct loader_instance *inst = loader.instances; inst; inst = inst->next) {
for (struct loader_icd *icd = inst->icds; icd; icd = icd->next) {
for (struct loader_device *dev = icd->logical_device_list; dev; dev = dev->next)
if (dev->device == device) {
*found_dev = dev;
return icd;
}
}
}
return NULL;
}
static void loader_destroy_logical_device(struct loader_device *dev)
{
free(dev->app_extension_props);
if (dev->enabled_device_extensions.count)
loader_destroy_ext_list(&dev->enabled_device_extensions);
if (dev->activated_layer_list.count)
loader_destroy_ext_list(&dev->activated_layer_list);
free(dev);
}
static struct loader_device *loader_add_logical_device(const VkDevice dev, struct loader_device **device_list)
{
struct loader_device *new_dev;
new_dev = malloc(sizeof(struct loader_device));
if (!new_dev) {
loader_log(VK_DBG_REPORT_ERROR_BIT, 0, "Failed to malloc struct laoder-device");
return NULL;
}
memset(new_dev, 0, sizeof(struct loader_device));
new_dev->next = *device_list;
new_dev->device = dev;
*device_list = new_dev;
return new_dev;
}
void loader_remove_logical_device(VkDevice device)
{
struct loader_device *found_dev, *dev, *prev_dev;
struct loader_icd *icd;
icd = loader_get_icd_and_device(device, &found_dev);
if (!icd || !found_dev)
return;
prev_dev = NULL;
dev = icd->logical_device_list;
while (dev && dev != found_dev) {
prev_dev = dev;
dev = dev->next;
}
if (prev_dev)
prev_dev->next = found_dev->next;
else
icd->logical_device_list = found_dev->next;
loader_destroy_logical_device(found_dev);
}
static void loader_icd_destroy(
struct loader_instance *ptr_inst,
struct loader_icd *icd)
{
ptr_inst->total_icd_count--;
free(icd->gpus);
for (struct loader_device *dev = icd->logical_device_list; dev; ) {
struct loader_device *next_dev = dev->next;
loader_destroy_logical_device(dev);
dev = next_dev;
}
free(icd);
}
static struct loader_icd * loader_icd_create(const struct loader_scanned_icds *scanned)
{
struct loader_icd *icd;
icd = malloc(sizeof(*icd));
if (!icd)
return NULL;
memset(icd, 0, sizeof(*icd));
icd->scanned_icds = scanned;
return icd;
}
static struct loader_icd *loader_icd_add(
struct loader_instance *ptr_inst,
const struct loader_scanned_icds *scanned)
{
struct loader_icd *icd;
icd = loader_icd_create(scanned);
if (!icd)
return NULL;
/* prepend to the list */
icd->next = ptr_inst->icds;
ptr_inst->icds = icd;
ptr_inst->total_icd_count++;
return icd;
}
static void loader_scanned_icd_add(const char *filename)
{
loader_platform_dl_handle handle;
void *fp_create_inst;
void *fp_get_global_ext_props;
void *fp_get_global_ext_count;
void *fp_get_device_ext_props;
void *fp_get_device_ext_count;
PFN_vkGPA fp_get_proc_addr;
struct loader_scanned_icds *new_node;
// Used to call: dlopen(filename, RTLD_LAZY);
handle = loader_platform_open_library(filename);
if (!handle) {
loader_log(VK_DBG_REPORT_WARN_BIT, 0, loader_platform_open_library_error(filename));
return;
}
#define LOOKUP(func_ptr, func) do { \
func_ptr = (PFN_vk ##func) loader_platform_get_proc_address(handle, "vk" #func); \
if (!func_ptr) { \
loader_log(VK_DBG_REPORT_WARN_BIT, 0, loader_platform_get_proc_address_error("vk" #func)); \
return; \
} \
} while (0)
LOOKUP(fp_create_inst, CreateInstance);
LOOKUP(fp_get_global_ext_props, GetGlobalExtensionProperties);
LOOKUP(fp_get_global_ext_count, GetGlobalExtensionCount);
LOOKUP(fp_get_device_ext_props, GetPhysicalDeviceExtensionProperties);
LOOKUP(fp_get_device_ext_count, GetPhysicalDeviceExtensionCount);
LOOKUP(fp_get_proc_addr, GetDeviceProcAddr);
#undef LOOKUP
new_node = (struct loader_scanned_icds *) malloc(sizeof(struct loader_scanned_icds)
+ strlen(filename) + 1);
if (!new_node) {
loader_log(VK_DBG_REPORT_WARN_BIT, 0, "Out of memory can't add icd");
return;
}
new_node->handle = handle;
new_node->CreateInstance = fp_create_inst;
new_node->GetGlobalExtensionProperties = fp_get_global_ext_props;
new_node->GetGlobalExtensionCount = fp_get_global_ext_count;
loader_init_ext_list(&new_node->global_extension_list);
loader_init_ext_list(&new_node->device_extension_list);
new_node->next = loader.scanned_icd_list;
new_node->lib_name = (char *) (new_node + 1);
if (!new_node->lib_name) {
loader_log(VK_DBG_REPORT_WARN_BIT, 0, "Out of memory can't add icd");
return;
}
strcpy(new_node->lib_name, filename);
loader.scanned_icd_list = new_node;
loader_get_global_extensions(
(PFN_vkGetGlobalExtensionCount) fp_get_global_ext_count,
(PFN_vkGetGlobalExtensionProperties) fp_get_global_ext_props,
fp_get_proc_addr,
new_node->lib_name,
handle,
VK_EXTENSION_ORIGIN_ICD,
&new_node->global_extension_list);
}
static void loader_icd_init_entrys(struct loader_icd *icd,
struct loader_scanned_icds *scanned_icds)
{
/* initialize entrypoint function pointers */
#define LOOKUP(func) do { \
icd->func = (PFN_vk ##func) loader_platform_get_proc_address(scanned_icds->handle, "vk" #func); \
if (!icd->func) { \
loader_log(VK_DBG_REPORT_WARN_BIT, 0, loader_platform_get_proc_address_error("vk" #func)); \
return; \
} \
} while (0)
/* could change this to use GetInstanceProcAddr in driver instead of dlsym */
LOOKUP(GetDeviceProcAddr);
LOOKUP(DestroyInstance);
LOOKUP(EnumeratePhysicalDevices);
LOOKUP(GetPhysicalDeviceFeatures);
LOOKUP(GetPhysicalDeviceFormatInfo);
LOOKUP(GetPhysicalDeviceLimits);
LOOKUP(CreateDevice);
LOOKUP(GetPhysicalDeviceProperties);
LOOKUP(GetPhysicalDeviceMemoryProperties);
LOOKUP(GetPhysicalDevicePerformance);
LOOKUP(GetPhysicalDeviceQueueCount);
LOOKUP(GetPhysicalDeviceQueueProperties);
LOOKUP(GetPhysicalDeviceExtensionProperties);
LOOKUP(GetPhysicalDeviceExtensionCount);
LOOKUP(DbgCreateMsgCallback);
LOOKUP(DbgDestroyMsgCallback);
#undef LOOKUP
return;
}
static void loader_debug_init(void)
{
const char *env;
if (g_loader_debug > 0)
return;
g_loader_debug = 0;
/* parse comma-separated debug options */
env = getenv("LOADER_DEBUG");
while (env) {
const char *p = strchr(env, ',');
size_t len;
if (p)
len = p - env;
else
len = strlen(env);
if (len > 0) {
if (strncmp(env, "warn", len) == 0) {
g_loader_debug |= LOADER_WARN_BIT;
g_loader_log_msgs |= VK_DBG_REPORT_WARN_BIT;
} else if (strncmp(env, "info", len) == 0) {
g_loader_debug |= LOADER_INFO_BIT;
g_loader_log_msgs |= VK_DBG_REPORT_INFO_BIT;
} else if (strncmp(env, "perf", len) == 0) {
g_loader_debug |= LOADER_PERF_BIT;
g_loader_log_msgs |= VK_DBG_REPORT_PERF_WARN_BIT;
} else if (strncmp(env, "error", len) == 0) {
g_loader_debug |= LOADER_ERROR_BIT;
g_loader_log_msgs |= VK_DBG_REPORT_ERROR_BIT;
} else if (strncmp(env, "debug", len) == 0) {
g_loader_debug |= LOADER_DEBUG_BIT;
g_loader_log_msgs |= VK_DBG_REPORT_DEBUG_BIT;
}
}
if (!p)
break;
env = p + 1;
}
}
/**
* Try to \c loader_icd_scan VK driver(s).
*
* This function scans the default system path or path
* specified by the \c LIBVK_DRIVERS_PATH environment variable in
* order to find loadable VK ICDs with the name of libVK_*.
*
* \returns
* void; but side effect is to set loader_icd_scanned to true
*/
void loader_icd_scan(void)
{
const char *p, *next;
char *libPaths = NULL;
DIR *sysdir;
struct dirent *dent;
char icd_library[1024];
char path[1024];
uint32_t len;
// convenient place to initialize a mutex
loader_platform_thread_create_mutex(&loader_lock);
#if defined(WIN32)
bool must_free_libPaths;
libPaths = loader_get_registry_and_env(DRIVER_PATH_ENV,
DRIVER_PATH_REGISTRY_VALUE);
if (libPaths != NULL) {
must_free_libPaths = true;
} else {
must_free_libPaths = false;
libPaths = DEFAULT_VK_DRIVERS_PATH;
}
#else // WIN32
if (geteuid() == getuid()) {
/* Don't allow setuid apps to use the DRIVER_PATH_ENV env var: */
libPaths = getenv(DRIVER_PATH_ENV);
}
if (libPaths == NULL) {
libPaths = DEFAULT_VK_DRIVERS_PATH;
}
#endif // WIN32
loader_debug_init();
for (p = libPaths; *p; p = next) {
next = strchr(p, PATH_SEPERATOR);
if (next == NULL) {
len = (uint32_t) strlen(p);
next = p + len;
}
else {
len = (uint32_t) (next - p);
sprintf(path, "%.*s", (len > sizeof(path) - 1) ? (int) sizeof(path) - 1 : len, p);
p = path;
next++;
}
// TODO/TBD: Do we want to do this on Windows, or just let Windows take
// care of its own search path (which it apparently has)?
sysdir = opendir(p);
if (sysdir) {
dent = readdir(sysdir);
while (dent) {
/* Look for ICDs starting with VK_DRIVER_LIBRARY_PREFIX and
* ending with VK_LIBRARY_SUFFIX
*/
if (!strncmp(dent->d_name,
VK_DRIVER_LIBRARY_PREFIX,
VK_DRIVER_LIBRARY_PREFIX_LEN)) {
uint32_t nlen = (uint32_t) strlen(dent->d_name);
const char *suf = dent->d_name + nlen - VK_LIBRARY_SUFFIX_LEN;
if ((nlen > VK_LIBRARY_SUFFIX_LEN) &&
!strncmp(suf,
VK_LIBRARY_SUFFIX,
VK_LIBRARY_SUFFIX_LEN)) {
snprintf(icd_library, 1024, "%s" DIRECTORY_SYMBOL "%s", p,dent->d_name);
loader_scanned_icd_add(icd_library);
}
}
dent = readdir(sysdir);
}
closedir(sysdir);
}
}
#if defined(WIN32)
// Free any allocated memory:
if (must_free_libPaths) {
free(libPaths);
}
#endif // WIN32
// Note that we've scanned for ICDs:
loader.icds_scanned = true;
}
void layer_lib_scan(void)
{
const char *p, *next;
char *libPaths = NULL;
DIR *curdir;
struct dirent *dent;
size_t len, i;
char temp_str[1024];
uint32_t count;
PFN_vkGetGlobalExtensionProperties fp_get_props;
PFN_vkGetGlobalExtensionCount fp_get_count;
#if defined(WIN32)
bool must_free_libPaths;
libPaths = loader_get_registry_and_env(LAYERS_PATH_ENV,
LAYERS_PATH_REGISTRY_VALUE);
if (libPaths != NULL) {
must_free_libPaths = true;
} else {
must_free_libPaths = false;
libPaths = DEFAULT_VK_LAYERS_PATH;
}
#else // WIN32
if (geteuid() == getuid()) {
/* Don't allow setuid apps to use the DRIVER_PATH_ENV env var: */
libPaths = getenv(LAYERS_PATH_ENV);
}
if (libPaths == NULL) {
libPaths = DEFAULT_VK_LAYERS_PATH;
}
#endif // WIN32
if (libPaths == NULL) {
// Have no paths to search:
return;
}
len = strlen(libPaths);
loader.layer_dirs = malloc(len+1);
if (loader.layer_dirs == NULL) {
loader_log(VK_DBG_REPORT_WARN_BIT, 0, "Out of memory can't add layer directories");
free(libPaths);
return;
}
// Alloc passed, so we know there is enough space to hold the string
strcpy(loader.layer_dirs, libPaths);
#if defined(WIN32)
// Free any allocated memory:
if (must_free_libPaths) {
free(libPaths);
must_free_libPaths = false;
}
#endif // WIN32
libPaths = loader.layer_dirs;
/* cleanup any previously scanned libraries */
for (i = 0; i < loader.scanned_layer_count; i++) {
if (loader.scanned_layers[i].lib_name != NULL)
free(loader.scanned_layers[i].lib_name);
loader_destroy_ext_list(&loader.scanned_layers[i].global_extension_list);
loader.scanned_layers[i].lib_name = NULL;
}
loader.scanned_layer_count = 0;
count = 0;
for (p = libPaths; *p; p = next) {
next = strchr(p, PATH_SEPERATOR);
if (next == NULL) {
len = (uint32_t) strlen(p);
next = p + len;
}
else {
len = (uint32_t) (next - p);
*(char *) next = '\0';
next++;
}
curdir = opendir(p);
if (curdir) {
dent = readdir(curdir);
while (dent) {
/* Look for layers starting with VK_LAYER_LIBRARY_PREFIX and
* ending with VK_LIBRARY_SUFFIX
*/
if (!strncmp(dent->d_name,
VK_LAYER_LIBRARY_PREFIX,
VK_LAYER_LIBRARY_PREFIX_LEN)) {
uint32_t nlen = (uint32_t) strlen(dent->d_name);
const char *suf = dent->d_name + nlen - VK_LIBRARY_SUFFIX_LEN;
if ((nlen > VK_LIBRARY_SUFFIX_LEN) &&
!strncmp(suf,
VK_LIBRARY_SUFFIX,
VK_LIBRARY_SUFFIX_LEN)) {
loader_platform_dl_handle handle;
snprintf(temp_str, sizeof(temp_str),
"%s" DIRECTORY_SYMBOL "%s",p,dent->d_name);
// Used to call: dlopen(temp_str, RTLD_LAZY)
loader_log(VK_DBG_REPORT_DEBUG_BIT, 0,
"Attempt to open library: %s", temp_str);
if ((handle = loader_platform_open_library(temp_str)) == NULL) {
loader_log(VK_DBG_REPORT_DEBUG_BIT, 0, "open library failed");
dent = readdir(curdir);
continue;
}
loader_log(VK_DBG_REPORT_DEBUG_BIT, 0,
"Opened library: %s", temp_str);
/* TODO: Remove fixed count */
if (count == MAX_LAYER_LIBRARIES) {
loader_log(VK_DBG_REPORT_ERROR_BIT, 0,
"%s ignored: max layer libraries exceed",
temp_str);
break;
}
fp_get_count = loader_platform_get_proc_address(handle, "vkGetGlobalExtensionCount");
fp_get_props = loader_platform_get_proc_address(handle, "vkGetGlobalExtensionProperties");
if (!fp_get_props || !fp_get_count) {
loader_log(VK_DBG_REPORT_WARN_BIT, 0,
"Couldn't dlsym vkGetGlobalExtensionCount and/or vkGetGlobalExtensionProperties from library %s",
temp_str);
dent = readdir(curdir);
loader_platform_close_library(handle);
continue;
}
loader.scanned_layers[count].lib_name =
malloc(strlen(temp_str) + 1);
if (loader.scanned_layers[count].lib_name == NULL) {
loader_log(VK_DBG_REPORT_ERROR_BIT, 0, "%s ignored: out of memory", temp_str);
break;
}
strcpy(loader.scanned_layers[count].lib_name, temp_str);
loader_log(VK_DBG_REPORT_DEBUG_BIT, 0, "Collecting global extensions for %s", temp_str);
loader_get_global_extensions(
fp_get_count,
fp_get_props,
NULL,
loader.scanned_layers[count].lib_name,
handle,
VK_EXTENSION_ORIGIN_LAYER,
&loader.scanned_layers[count].global_extension_list);
fp_get_count = loader_platform_get_proc_address(handle,
"vkGetPhysicalDeviceExtensionCount");
fp_get_props = loader_platform_get_proc_address(handle,
"vkGetPhysicalDeviceExtensionProperties");
if (fp_get_props && fp_get_count) {
loader.scanned_layers[count].physical_device_extensions_supported = true;
}
count++;
loader_platform_close_library(handle);
}
}
dent = readdir(curdir);
} // while (dir_entry)
if (count == MAX_LAYER_LIBRARIES)
break;
closedir(curdir);
} // if (curdir))
} // for (libpaths)
loader.scanned_layer_count = count;
loader.layers_scanned = true;
}
static void* VKAPI loader_gpa_instance_internal(VkInstance inst, const char * pName)
{
// inst is not wrapped
if (inst == VK_NULL_HANDLE) {
return NULL;
}
VkLayerInstanceDispatchTable* disp_table = * (VkLayerInstanceDispatchTable **) inst;
void *addr;
if (!strcmp(pName, "vkGetInstanceProcAddr"))
return (void *) loader_gpa_instance_internal;
if (disp_table == NULL)
return NULL;
addr = loader_lookup_instance_dispatch_table(disp_table, pName);
if (addr) {
return addr;
}
if (disp_table->GetInstanceProcAddr == NULL) {
return NULL;
}
return disp_table->GetInstanceProcAddr(inst, pName);
}
struct loader_icd * loader_get_icd(const VkPhysicalDevice gpu, uint32_t *gpu_index)
{
for (struct loader_instance *inst = loader.instances; inst; inst = inst->next) {
for (struct loader_icd *icd = inst->icds; icd; icd = icd->next) {
for (uint32_t i = 0; i < icd->gpu_count; i++)
if (icd->gpus[i] == gpu) {
*gpu_index = i;
return icd;
}
}
}
return NULL;
}
static loader_platform_dl_handle loader_add_layer_lib(
const char *chain_type,
struct loader_extension_property *ext_prop)
{
struct loader_lib_info *new_layer_lib_list, *my_lib;
/* Only loader layer libraries here */
if (ext_prop->origin != VK_EXTENSION_ORIGIN_LAYER) {
return NULL;
}
for (uint32_t i = 0; i < loader.loaded_layer_lib_count; i++) {
if (strcmp(loader.loaded_layer_lib_list[i].lib_name, ext_prop->lib_name) == 0) {
/* Have already loaded this library, just increment ref count */
loader.loaded_layer_lib_list[i].ref_count++;
loader_log(VK_DBG_REPORT_DEBUG_BIT, 0,
"%s Chain: Increment layer reference count for layer library %s",
chain_type, ext_prop->lib_name);
return loader.loaded_layer_lib_list[i].lib_handle;
}
}
/* Haven't seen this library so load it */
new_layer_lib_list = realloc(loader.loaded_layer_lib_list,
(loader.loaded_layer_lib_count + 1) * sizeof(struct loader_lib_info));
if (!new_layer_lib_list) {
loader_log(VK_DBG_REPORT_ERROR_BIT, 0, "loader: malloc failed");
return NULL;
}
my_lib = &new_layer_lib_list[loader.loaded_layer_lib_count];
/* NOTE: We require that the extension property be immutable */
my_lib->lib_name = ext_prop->lib_name;
my_lib->ref_count = 0;
my_lib->lib_handle = NULL;
if ((my_lib->lib_handle = loader_platform_open_library(my_lib->lib_name)) == NULL) {
loader_log(VK_DBG_REPORT_ERROR_BIT, 0,
loader_platform_open_library_error(my_lib->lib_name));
return NULL;
} else {
loader_log(VK_DBG_REPORT_DEBUG_BIT, 0,
"Chain: %s: Loading layer library %s",
chain_type, ext_prop->lib_name);
}
loader.loaded_layer_lib_count++;
loader.loaded_layer_lib_list = new_layer_lib_list;
my_lib->ref_count++;
return my_lib->lib_handle;
}
static void loader_remove_layer_lib(
struct loader_instance *inst,
struct loader_extension_property *ext_prop)
{
uint32_t idx;
struct loader_lib_info *new_layer_lib_list, *my_lib;
/* Only loader layer libraries here */
if (ext_prop->origin != VK_EXTENSION_ORIGIN_LAYER) {
return;
}
for (uint32_t i = 0; i < loader.loaded_layer_lib_count; i++) {
if (strcmp(loader.loaded_layer_lib_list[i].lib_name, ext_prop->lib_name) == 0) {
/* found matching library */
idx = i;
my_lib = &loader.loaded_layer_lib_list[i];
break;
}
}
my_lib->ref_count--;
inst->layer_count--;
if (my_lib->ref_count > 0) {
loader_log(VK_DBG_REPORT_DEBUG_BIT, 0,
"Decrement reference count for layer library %s", ext_prop->lib_name);
return;
}
loader_platform_close_library(my_lib->lib_handle);
loader_log(VK_DBG_REPORT_DEBUG_BIT, 0,
"Unloading layer library %s", ext_prop->lib_name);
/* Need to remove unused library from list */
new_layer_lib_list = malloc((loader.loaded_layer_lib_count - 1) * sizeof(struct loader_lib_info));
if (!new_layer_lib_list) {
loader_log(VK_DBG_REPORT_ERROR_BIT, 0, "loader: malloc failed");
return;
}
if (idx > 0) {
/* Copy records before idx */
memcpy(new_layer_lib_list, &loader.loaded_layer_lib_list[0],
sizeof(struct loader_lib_info) * idx);
}
if (idx < (loader.loaded_layer_lib_count - 1)) {
/* Copy records after idx */
memcpy(&new_layer_lib_list[idx], &loader.loaded_layer_lib_list[idx+1],
sizeof(struct loader_lib_info) * (loader.loaded_layer_lib_count - idx - 1));
}
free(loader.loaded_layer_lib_list);
loader.loaded_layer_lib_count--;
loader.loaded_layer_lib_list = new_layer_lib_list;
}
static void loader_add_layer_env(
struct loader_extension_list *ext_list,
const struct loader_extension_list *search_list)
{
char *layerEnv;
uint32_t len;
char *p, *pOrig, *next, *name;
#if defined(WIN32)
layerEnv = loader_get_registry_and_env(LAYER_NAMES_ENV,
LAYER_NAMES_REGISTRY_VALUE);
#else // WIN32
layerEnv = getenv(LAYER_NAMES_ENV);
#endif // WIN32
if (layerEnv == NULL) {
return;
}
p = malloc(strlen(layerEnv) + 1);
if (p == NULL) {
#if defined(WIN32)
free(layerEnv);
#endif // WIN32
return;
}
strcpy(p, layerEnv);
#if defined(WIN32)
free(layerEnv);
#endif // WIN32
pOrig = p;
while (p && *p ) {
next = strchr(p, PATH_SEPERATOR);
if (next == NULL) {
len = (uint32_t) strlen(p);
next = p + len;
} else {
len = (uint32_t) (next - p);
*(char *) next = '\0';
next++;
}
name = basename(p);
loader_search_ext_list_for_name(name, search_list, ext_list);
p = next;
}
free(pOrig);
return;
}
void loader_deactivate_instance_layers(struct loader_instance *instance)
{
if (!instance->layer_count) {
return;
}
/* Create instance chain of enabled layers */
for (uint32_t i = 0; i < instance->activated_layer_list.count; i++) {
struct loader_extension_property *ext_prop = &instance->activated_layer_list.list[i];
loader_remove_layer_lib(instance, ext_prop);
instance->layer_count--;
}
loader_destroy_ext_list(&instance->activated_layer_list);
}
void loader_enable_instance_layers(struct loader_instance *inst)
{
if (inst == NULL)
return;
/* Add any layers specified in the environment first */
loader_add_layer_env(&inst->enabled_instance_extensions, &loader.global_extensions);
/* Add layers / extensions specified by the application */
loader_add_vk_ext_to_ext_list(
&inst->enabled_instance_extensions,
inst->app_extension_count,
inst->app_extension_props,
&loader.global_extensions);
}
uint32_t loader_activate_instance_layers(struct loader_instance *inst)
{
uint32_t layer_idx;
VkBaseLayerObject *wrappedInstance;
if (inst == NULL) {
return 0;
}
// NOTE inst is unwrapped at this point in time
VkObject baseObj = (VkObject) inst;
VkObject nextObj = (VkObject) inst;
VkBaseLayerObject *nextInstObj;
PFN_vkGetInstanceProcAddr nextGPA = loader_gpa_instance_internal;
/*
* Figure out how many actual layers will need to be wrapped.
*/
for (uint32_t i = 0; i < inst->enabled_instance_extensions.count; i++) {
struct loader_extension_property *ext_prop = &inst->enabled_instance_extensions.list[i];
if (ext_prop->alias) {
ext_prop = ext_prop->alias;
}
if (ext_prop->origin != VK_EXTENSION_ORIGIN_LAYER) {
continue;
}
loader_add_to_ext_list(&inst->activated_layer_list, 1, ext_prop);
}
inst->layer_count = inst->activated_layer_list.count;
if (!inst->layer_count) {
return 0;
}
wrappedInstance = malloc(sizeof(VkBaseLayerObject)
* inst->layer_count);
if (!wrappedInstance) {
loader_log(VK_DBG_REPORT_ERROR_BIT, 0, "Failed to malloc Instance objects for layer");
return 0;
}
/* Create instance chain of enabled layers */
layer_idx = inst->layer_count - 1;
for (int32_t i = inst->activated_layer_list.count - 1; i >= 0; i--) {
struct loader_extension_property *ext_prop = &inst->activated_layer_list.list[i];
loader_platform_dl_handle lib_handle;
/*
* For global exenstions implemented within the loader (i.e. DEBUG_REPORT
* the extension must provide two entry points for the loader to use:
* - "trampoline" entry point - this is the address returned by GetProcAddr
* and will always do what's necessary to support a global call.
* - "terminator" function - this function will be put at the end of the
* instance chain and will contain the necessary logica to call / process
* the extension for the appropriate ICDs that are available.
* There is no generic mechanism for including these functions, the references
* must be placed into the appropriate loader entry points.
* GetInstanceProcAddr: call extension GetInstanceProcAddr to check for GetProcAddr requests
* loader_coalesce_extensions(void) - add extension records to the list of global
* extension available to the app.
* instance_disp - add function pointer for terminator function to this array.
* The extension itself should be in a separate file that will be
* linked directly with the loader.
* Note: An extension's Get*ProcAddr should not return a function pointer for
* any extension entry points until the extension has been enabled.
* To do this requires a different behavior from Get*ProcAddr functions implemented
* in layers.
* The very first call to a layer will be it's Get*ProcAddr function requesting
* the layer's vkGet*ProcAddr. The layer should intialize it's internal dispatch table
* with the wrapped object given (either Instance or Device) and return the layer's
* Get*ProcAddr function. The layer should also use this opportunity to record the
* baseObject so that it can find the correct local dispatch table on future calls.
* Subsequent calls to Get*ProcAddr, CreateInstance, CreateDevice
* will not use a wrapped object and must look up their local dispatch table from
* the given baseObject.
*/
assert(ext_prop->origin == VK_EXTENSION_ORIGIN_LAYER);
nextInstObj = (wrappedInstance + layer_idx);
nextInstObj->pGPA = nextGPA;
nextInstObj->baseObject = baseObj;
nextInstObj->nextObject = nextObj;
nextObj = (VkObject) nextInstObj;
char funcStr[256];
snprintf(funcStr, 256, "%sGetInstanceProcAddr", ext_prop->info.name);
lib_handle = loader_add_layer_lib("instance", ext_prop);
if ((nextGPA = (PFN_vkGetInstanceProcAddr) loader_platform_get_proc_address(lib_handle, funcStr)) == NULL)
nextGPA = (PFN_vkGetInstanceProcAddr) loader_platform_get_proc_address(lib_handle, "vkGetInstanceProcAddr");
if (!nextGPA) {
loader_log(VK_DBG_REPORT_ERROR_BIT, 0, "Failed to find vkGetInstanceProcAddr in layer %s", ext_prop->lib_name);
/* TODO: Should we return nextObj, nextGPA to previous? */
continue;
}
loader_log(VK_DBG_REPORT_INFO_BIT, 0,
"Insert instance layer library %s for extension: %s",
ext_prop->lib_name, ext_prop->info.name);
layer_idx--;
}
loader_init_instance_core_dispatch_table(inst->disp, nextGPA, (VkInstance) nextObj, (VkInstance) baseObj);
free(wrappedInstance);
return inst->layer_count;
}
void loader_activate_instance_layer_extensions(struct loader_instance *inst)
{
loader_init_instance_extension_dispatch_table(inst->disp,
inst->disp->GetInstanceProcAddr,
(VkInstance) inst);
}
static void loader_enable_device_layers(struct loader_device *dev)
{
if (dev == NULL)
return;
/* Add any layers specified in the environment first */
loader_add_layer_env(&dev->enabled_device_extensions, &loader.global_extensions);
/* Add layers / extensions specified by the application */
loader_add_vk_ext_to_ext_list(
&dev->enabled_device_extensions,
dev->app_extension_count,
dev->app_extension_props,
&loader.global_extensions);
}
static uint32_t loader_activate_device_layers(
VkDevice device,
struct loader_device *dev,
struct loader_icd *icd,
uint32_t ext_count,
const VkExtensionProperties *ext_props)
{
if (!icd)
return 0;
if (!dev) {
return 0;
}
/* activate any layer libraries */
VkObject nextObj = (VkObject) device;
VkObject baseObj = nextObj;
VkBaseLayerObject *nextGpuObj;
PFN_vkGetDeviceProcAddr nextGPA = icd->GetDeviceProcAddr;
VkBaseLayerObject *wrappedGpus;
/*
* Figure out how many actual layers will need to be wrapped.
*/
for (uint32_t i = 0; i < dev->enabled_device_extensions.count; i++) {
struct loader_extension_property *ext_prop = &dev->enabled_device_extensions.list[i];
if (ext_prop->alias) {
ext_prop = ext_prop->alias;
}
if (ext_prop->origin != VK_EXTENSION_ORIGIN_LAYER) {
continue;
}
loader_add_to_ext_list(&dev->activated_layer_list, 1, ext_prop);
}
if (!dev->activated_layer_list.count)
return 0;
wrappedGpus = malloc(sizeof (VkBaseLayerObject) * dev->activated_layer_list.count);
if (!wrappedGpus) {
loader_log(VK_DBG_REPORT_ERROR_BIT, 0, "Failed to malloc Gpu objects for layer");
return 0;
}
for (int32_t i = dev->activated_layer_list.count - 1; i >= 0; i--) {
struct loader_extension_property *ext_prop = &dev->activated_layer_list.list[i];
loader_platform_dl_handle lib_handle;
assert(ext_prop->origin == VK_EXTENSION_ORIGIN_LAYER);
nextGpuObj = (wrappedGpus + i);
nextGpuObj->pGPA = nextGPA;
nextGpuObj->baseObject = baseObj;
nextGpuObj->nextObject = nextObj;
nextObj = (VkObject) nextGpuObj;
char funcStr[256];
snprintf(funcStr, 256, "%sGetDeviceProcAddr", ext_prop->info.name);
lib_handle = loader_add_layer_lib("device", ext_prop);
if ((nextGPA = (PFN_vkGetDeviceProcAddr) loader_platform_get_proc_address(lib_handle, funcStr)) == NULL)
nextGPA = (PFN_vkGetDeviceProcAddr) loader_platform_get_proc_address(lib_handle, "vkGetDeviceProcAddr");
if (!nextGPA) {
loader_log(VK_DBG_REPORT_ERROR_BIT, 0, "Failed to find vkGetDeviceProcAddr in layer %s", ext_prop->info.name);
continue;
}
loader_log(VK_DBG_REPORT_INFO_BIT, 0,
"Insert device layer library %s for extension: %s",
ext_prop->lib_name, ext_prop->info.name);
}
loader_init_device_dispatch_table(&dev->loader_dispatch, nextGPA,
(VkPhysicalDevice) nextObj, (VkPhysicalDevice) baseObj);
free(wrappedGpus);
return dev->activated_layer_list.count;
}
VkResult loader_CreateInstance(
const VkInstanceCreateInfo* pCreateInfo,
VkInstance* pInstance)
{
struct loader_instance *ptr_instance = *(struct loader_instance **) pInstance;
struct loader_scanned_icds *scanned_icds;
struct loader_icd *icd;
VkResult res;
scanned_icds = loader.scanned_icd_list;
while (scanned_icds) {
icd = loader_icd_add(ptr_instance, scanned_icds);
if (icd) {
res = scanned_icds->CreateInstance(pCreateInfo,
&(icd->instance));
if (res != VK_SUCCESS)
{
ptr_instance->icds = ptr_instance->icds->next;
loader_icd_destroy(ptr_instance, icd);
icd->instance = VK_NULL_HANDLE;
loader_log(VK_DBG_REPORT_WARN_BIT, 0,
"ICD ignored: failed to CreateInstance on device");
} else
{
loader_icd_init_entrys(icd, scanned_icds);
}
}
scanned_icds = scanned_icds->next;
}
if (ptr_instance->icds == NULL) {
return VK_ERROR_INCOMPATIBLE_DRIVER;
}
return res;
}
VkResult loader_DestroyInstance(
VkInstance instance)
{
struct loader_instance *ptr_instance = loader_instance(instance);
struct loader_icd *icds = ptr_instance->icds;
struct loader_icd *next_icd;
VkResult res;
// Remove this instance from the list of instances:
struct loader_instance *prev = NULL;
struct loader_instance *next = loader.instances;
while (next != NULL) {
if (next == ptr_instance) {
// Remove this instance from the list:
free(ptr_instance->app_extension_props);
if (prev)
prev->next = next->next;
else
loader.instances = next->next;
break;
}
prev = next;
next = next->next;
}
if (next == NULL) {
// This must be an invalid instance handle or empty list
return VK_ERROR_INVALID_HANDLE;
}
while (icds) {
if (icds->instance) {
res = icds->DestroyInstance(icds->instance);
if (res != VK_SUCCESS)
loader_log(VK_DBG_REPORT_WARN_BIT, 0,
"ICD ignored: failed to DestroyInstance on device");
}
next_icd = icds->next;
icds->instance = VK_NULL_HANDLE;
loader_icd_destroy(ptr_instance, icds);
icds = next_icd;
}
return VK_SUCCESS;
}
VkResult loader_init_physical_device_info(
struct loader_instance *ptr_instance)
{
struct loader_icd *icd;
uint32_t n, count = 0;
VkResult res = VK_ERROR_UNKNOWN;
icd = ptr_instance->icds;
while (icd) {
res = icd->EnumeratePhysicalDevices(icd->instance, &n, NULL);
if (res != VK_SUCCESS)
return res;
icd->gpu_count = n;
count += n;
icd = icd->next;
}
ptr_instance->total_gpu_count = count;
icd = ptr_instance->icds;
while (icd) {
n = icd->gpu_count;
icd->gpus = (VkPhysicalDevice *) malloc(n * sizeof(VkPhysicalDevice));
if (!icd->gpus) {
/* TODO: Add cleanup code here */
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
res = icd->EnumeratePhysicalDevices(
icd->instance,
&n,
icd->gpus);
if ((res == VK_SUCCESS) && (n == icd->gpu_count)) {
for (unsigned int i = 0; i < n; i++) {
loader_init_dispatch(icd->gpus[i], ptr_instance->disp);
if (!loader_init_ext_list(&icd->device_extension_cache[i])) {
/* TODO: Add cleanup code here */
res = VK_ERROR_OUT_OF_HOST_MEMORY;
}
if (res == VK_SUCCESS && icd->GetPhysicalDeviceExtensionProperties && icd->GetPhysicalDeviceExtensionCount) {
uint32_t extension_count;
res = icd->GetPhysicalDeviceExtensionCount(icd->gpus[i], &extension_count);
if (res == VK_SUCCESS) {
struct loader_extension_property ext_props;
/* Gather all the ICD extensions */
for (uint32_t extension_id = 0; extension_id < extension_count; extension_id++) {
res = icd->GetPhysicalDeviceExtensionProperties(icd->gpus[i],
extension_id, &ext_props.info);
if (res == VK_SUCCESS) {
ext_props.origin = VK_EXTENSION_ORIGIN_ICD;
ext_props.lib_name = icd->scanned_icds->lib_name;
ext_props.get_proc_addr = icd->GetDeviceProcAddr;
loader_add_to_ext_list(&icd->device_extension_cache[i], 1, &ext_props);
}
}
// Traverse layers list adding non-duplicate extensions to the list
for (uint32_t l = 0; l < loader.scanned_layer_count; l++) {
loader_get_physical_device_layer_extensions(ptr_instance, icd->gpus[i], l, &icd->device_extension_cache[i]);
}
}
}
if (res != VK_SUCCESS) {
/* clean up any extension lists previously created before this request failed */
for (uint32_t j = 0; j < i; j++) {
loader_destroy_ext_list(&icd->device_extension_cache[i]);
}
return res;
}
}
count += n;
}
icd = icd->next;
}
return VK_SUCCESS;
}
VkResult loader_EnumeratePhysicalDevices(
VkInstance instance,
uint32_t* pPhysicalDeviceCount,
VkPhysicalDevice* pPhysicalDevices)
{
uint32_t index = 0;
struct loader_instance *ptr_instance = (struct loader_instance *) instance;
struct loader_icd *icd = ptr_instance->icds;
if (ptr_instance->total_gpu_count == 0) {
loader_init_physical_device_info(ptr_instance);
}
*pPhysicalDeviceCount = ptr_instance->total_gpu_count;
if (!pPhysicalDevices) {
return VK_SUCCESS;
}
while (icd) {
assert((index + icd->gpu_count) <= *pPhysicalDeviceCount);
memcpy(&pPhysicalDevices[index], icd->gpus, icd->gpu_count * sizeof(VkPhysicalDevice));
index += icd->gpu_count;
icd = icd->next;
}
return VK_SUCCESS;
}
VkResult loader_GetPhysicalDeviceProperties(
VkPhysicalDevice gpu,
VkPhysicalDeviceProperties* pProperties)
{
uint32_t gpu_index;
struct loader_icd *icd = loader_get_icd(gpu, &gpu_index);
VkResult res = VK_ERROR_INITIALIZATION_FAILED;
if (icd->GetPhysicalDeviceProperties)
res = icd->GetPhysicalDeviceProperties(gpu, pProperties);
return res;
}
VkResult loader_GetPhysicalDevicePerformance(
VkPhysicalDevice gpu,
VkPhysicalDevicePerformance* pPerformance)
{
uint32_t gpu_index;
struct loader_icd *icd = loader_get_icd(gpu, &gpu_index);
VkResult res = VK_ERROR_INITIALIZATION_FAILED;
if (icd->GetPhysicalDevicePerformance)
res = icd->GetPhysicalDevicePerformance(gpu, pPerformance);
return res;
}
VkResult loader_GetPhysicalDeviceQueueCount(
VkPhysicalDevice gpu,
uint32_t* pCount)
{
uint32_t gpu_index;
struct loader_icd *icd = loader_get_icd(gpu, &gpu_index);
VkResult res = VK_ERROR_INITIALIZATION_FAILED;
if (icd->GetPhysicalDeviceQueueCount)
res = icd->GetPhysicalDeviceQueueCount(gpu, pCount);
return res;
}
VkResult loader_GetPhysicalDeviceQueueProperties (
VkPhysicalDevice gpu,
uint32_t count,
VkPhysicalDeviceQueueProperties * pProperties)
{
uint32_t gpu_index;
struct loader_icd *icd = loader_get_icd(gpu, &gpu_index);
VkResult res = VK_ERROR_INITIALIZATION_FAILED;
if (icd->GetPhysicalDeviceQueueProperties)
res = icd->GetPhysicalDeviceQueueProperties(gpu, count, pProperties);
return res;
}
VkResult loader_GetPhysicalDeviceMemoryProperties (
VkPhysicalDevice gpu,
VkPhysicalDeviceMemoryProperties* pProperties)
{
uint32_t gpu_index;
struct loader_icd *icd = loader_get_icd(gpu, &gpu_index);
VkResult res = VK_ERROR_INITIALIZATION_FAILED;
if (icd->GetPhysicalDeviceMemoryProperties)
res = icd->GetPhysicalDeviceMemoryProperties(gpu, pProperties);
return res;
}
VkResult loader_GetPhysicalDeviceFeatures(
VkPhysicalDevice physicalDevice,
VkPhysicalDeviceFeatures* pFeatures)
{
uint32_t gpu_index;
struct loader_icd *icd = loader_get_icd(physicalDevice, &gpu_index);
VkResult res = VK_ERROR_INITIALIZATION_FAILED;
if (icd->GetPhysicalDeviceFeatures)
res = icd->GetPhysicalDeviceFeatures(physicalDevice, pFeatures);
return res;
}
VkResult loader_GetPhysicalDeviceFormatInfo(
VkPhysicalDevice physicalDevice,
VkFormat format,
VkFormatProperties* pFormatInfo)
{
uint32_t gpu_index;
struct loader_icd *icd = loader_get_icd(physicalDevice, &gpu_index);
VkResult res = VK_ERROR_INITIALIZATION_FAILED;
if (icd->GetPhysicalDeviceFormatInfo)
res = icd->GetPhysicalDeviceFormatInfo(physicalDevice, format, pFormatInfo);
return res;
}
VkResult loader_GetPhysicalDeviceLimits(
VkPhysicalDevice physicalDevice,
VkPhysicalDeviceLimits* pLimits)
{
uint32_t gpu_index;
struct loader_icd *icd = loader_get_icd(physicalDevice, &gpu_index);
VkResult res = VK_ERROR_INITIALIZATION_FAILED;
if (icd->GetPhysicalDeviceLimits)
res = icd->GetPhysicalDeviceLimits(physicalDevice, pLimits);
return res;
}
VkResult loader_CreateDevice(
VkPhysicalDevice gpu,
const VkDeviceCreateInfo* pCreateInfo,
VkDevice* pDevice)
{
uint32_t gpu_index;
struct loader_icd *icd = loader_get_icd(gpu, &gpu_index);
struct loader_device *dev;
VkResult res = VK_ERROR_INITIALIZATION_FAILED;
if (icd->CreateDevice) {
res = icd->CreateDevice(gpu, pCreateInfo, pDevice);
if (res != VK_SUCCESS) {
return res;
}
dev = loader_add_logical_device(*pDevice, &icd->logical_device_list);
if (dev == NULL) {
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
PFN_vkGetDeviceProcAddr get_proc_addr = icd->GetDeviceProcAddr;
loader_init_device_dispatch_table(&dev->loader_dispatch, get_proc_addr,
icd->gpus[gpu_index], icd->gpus[gpu_index]);
loader_init_dispatch(*pDevice, &dev->loader_dispatch);
dev->app_extension_count = pCreateInfo->extensionCount;
dev->app_extension_props = (VkExtensionProperties *) malloc(sizeof(VkExtensionProperties) * pCreateInfo->extensionCount);
if (dev->app_extension_props == NULL && (dev->app_extension_count > 0)) {
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
/* Make local copy of extension list */
if (dev->app_extension_count > 0 && dev->app_extension_props != NULL) {
memcpy(dev->app_extension_props, pCreateInfo->pEnabledExtensions, sizeof(VkExtensionProperties) * pCreateInfo->extensionCount);
}
/*
* Put together the complete list of extensions to enable
* This includes extensions requested via environment variables.
*/
loader_enable_device_layers(dev);
/*
* Load the libraries needed by the extensions on the
* enabled extension list. This will build the device chain
* terminating with the selected device.
*/
loader_activate_device_layers(*pDevice, dev, icd,
dev->app_extension_count,
dev->app_extension_props);
}
return res;
}
static void * VKAPI loader_GetInstanceProcAddr(VkInstance instance, const char * pName)
{
if (instance == VK_NULL_HANDLE)
return NULL;
void *addr;
/* get entrypoint addresses that are global (in the loader)*/
addr = globalGetProcAddr(pName);
if (addr)
return addr;
struct loader_instance *ptr_instance = (struct loader_instance *) instance;
/* return any extension global entrypoints */
addr = debug_report_instance_gpa(ptr_instance, pName);
if (addr) {
return addr;
}
/* TODO Remove this once WSI has no loader special code */
addr = wsi_lunarg_GetInstanceProcAddr(instance, pName);
if (addr) {
return addr;
}
/* return the instance dispatch table entrypoint for extensions */
const VkLayerInstanceDispatchTable *disp_table = * (VkLayerInstanceDispatchTable **) instance;
if (disp_table == NULL)
return NULL;
addr = loader_lookup_instance_dispatch_table(disp_table, pName);
if (addr)
return addr;
return NULL;
}
LOADER_EXPORT void * VKAPI vkGetInstanceProcAddr(VkInstance instance, const char * pName)
{
return loader_GetInstanceProcAddr(instance, pName);
}
static void * VKAPI loader_GetDeviceProcAddr(VkDevice device, const char * pName)
{
if (device == VK_NULL_HANDLE) {
return NULL;
}
void *addr;
/* for entrypoints that loader must handle (ie non-dispatchable or create object)
make sure the loader entrypoint is returned */
addr = loader_non_passthrough_gpa(pName);
if (addr) {
return addr;
}
/* return any extension device entrypoints the loader knows about */
/* TODO once WSI has no loader special code remove this */
addr = wsi_lunarg_GetDeviceProcAddr(device, pName);
if (addr) {
return addr;
}
/* return the dispatch table entrypoint for the fastest case */
const VkLayerDispatchTable *disp_table = * (VkLayerDispatchTable **) device;
if (disp_table == NULL)
return NULL;
addr = loader_lookup_device_dispatch_table(disp_table, pName);
if (addr)
return addr;
else {
if (disp_table->GetDeviceProcAddr == NULL)
return NULL;
return disp_table->GetDeviceProcAddr(device, pName);
}
}
LOADER_EXPORT void * VKAPI vkGetDeviceProcAddr(VkDevice device, const char * pName)
{
return loader_GetDeviceProcAddr(device, pName);
}
LOADER_EXPORT VkResult VKAPI vkGetGlobalExtensionCount(
uint32_t* pCount)
{
/* Scan/discover all ICD libraries in a single-threaded manner */
loader_platform_thread_once(&once_icd, loader_icd_scan);
/* get layer libraries in a single-threaded manner */
loader_platform_thread_once(&once_layer, layer_lib_scan);
/* merge any duplicate extensions */
loader_platform_thread_once(&once_exts, loader_coalesce_extensions);
loader_platform_thread_lock_mutex(&loader_lock);
*pCount = loader.global_extensions.count;
loader_platform_thread_unlock_mutex(&loader_lock);
return VK_SUCCESS;
}
LOADER_EXPORT VkResult VKAPI vkGetGlobalExtensionProperties(
uint32_t extensionIndex,
VkExtensionProperties* pProperties)
{
/* Scan/discover all ICD libraries in a single-threaded manner */
loader_platform_thread_once(&once_icd, loader_icd_scan);
/* get layer libraries in a single-threaded manner */
loader_platform_thread_once(&once_layer, layer_lib_scan);
/* merge any duplicate extensions */
loader_platform_thread_once(&once_exts, loader_coalesce_extensions);
if (extensionIndex >= loader.global_extensions.count)
return VK_ERROR_INVALID_VALUE;
loader_platform_thread_lock_mutex(&loader_lock);
memcpy(pProperties,
&loader.global_extensions.list[extensionIndex],
sizeof(VkExtensionProperties));
loader_platform_thread_unlock_mutex(&loader_lock);
return VK_SUCCESS;
}
VkResult loader_GetPhysicalDeviceExtensionCount(
VkPhysicalDevice gpu,
uint32_t* pCount)
{
uint32_t gpu_index;
struct loader_icd *icd = loader_get_icd(gpu, &gpu_index);
*pCount = icd->device_extension_cache[gpu_index].count;
return VK_SUCCESS;
}
VkResult loader_GetPhysicalDeviceExtensionProperties(
VkPhysicalDevice gpu,
uint32_t extensionIndex,
VkExtensionProperties* pProperties)
{
uint32_t gpu_index;
struct loader_icd *icd = loader_get_icd(gpu, &gpu_index);
if (extensionIndex >= icd->device_extension_cache[gpu_index].count)
return VK_ERROR_INVALID_VALUE;
memcpy( pProperties,
&icd->device_extension_cache[gpu_index].list[extensionIndex],
sizeof(VkExtensionProperties));
return VK_SUCCESS;
}