blob: 332912754cc2c5e34895f4014f145ab3c30bf889 [file] [edit]
/*
* Vulkan
*
* Copyright (C) 2016-2022 Valve Corporation
* Copyright (C) 2016-2022 LunarG, Inc.
* Copyright (C) 2016-2022 Google Inc.
*
* 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.
*
* Author: Chris Forbes <chrisforbes@google.com>
* Author: Tony Barbour <tony@lunarg.com>
*/
#include "vk_layer_table.h"
#include <vulkan/layer/vk_layer_settings.hpp>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unordered_map>
#include <vulkan/vulkan.h>
#if defined(__linux__)
#include <dlfcn.h>
#endif
#if (!defined(VK_USE_PLATFORM_XCB_KHR) && !defined(VK_USE_PLATFORM_WIN32_KHR))
#warning "Monitor layer only has code for XCB and Windows at this time"
#endif
#if defined(_WIN32) && !defined(NDEBUG)
#include <crtdbg.h>
#endif
#define TITLE_LENGTH 1000
#define FPS_LENGTH 24
struct monitor_layer_data {
VkuDeviceDispatchTable *device_dispatch_table{};
VkuInstanceDispatchTable *instance_dispatch_table{};
PFN_vkQueuePresentKHR pfnQueuePresentKHR{};
#if defined(VK_USE_PLATFORM_WIN32_KHR)
HWND hwnd{};
#elif defined(VK_USE_PLATFORM_XCB_KHR)
xcb_connection_t *connection{};
xcb_window_t xcb_window{};
bool xcb_fps{};
#endif
char base_title[TITLE_LENGTH]{};
bool got_title = false;
VkPhysicalDevice gpu{};
VkDevice device{};
PFN_vkSetDeviceLoaderData pfn_dev_init{};
int lastFrame{};
time_t lastTime{};
float fps{};
int frame{};
};
#if defined(VK_USE_PLATFORM_XCB_KHR)
static struct {
void *xcbLib{};
decltype(xcb_change_property) *change_property{};
decltype(xcb_flush) *flush{};
decltype(xcb_get_property) *get_property{};
decltype(xcb_get_property_reply) *get_property_reply{};
decltype(xcb_get_property_value_length) *get_property_value_length{};
decltype(xcb_get_property_value) *get_property_value{};
} xcb = {NULL};
#endif
static std::unordered_map<VkPhysicalDevice, VkInstance> layer_instances;
static std::unordered_map<void *, monitor_layer_data *> layer_data_map;
template monitor_layer_data *GetLayerDataPtr<monitor_layer_data>(void *data_key,
std::unordered_map<void *, monitor_layer_data *> &data_map);
VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice gpu, const VkDeviceCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) {
VkLayerDeviceCreateInfo *chain_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
assert(chain_info->u.pLayerInfo);
PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr;
PFN_vkCreateDevice fpCreateDevice = (PFN_vkCreateDevice)fpGetInstanceProcAddr(layer_instances.at(gpu), "vkCreateDevice");
if (fpCreateDevice == NULL) {
return VK_ERROR_INITIALIZATION_FAILED;
}
// Advance the link info for the next element on the chain
chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
VkResult result = fpCreateDevice(gpu, pCreateInfo, pAllocator, pDevice);
if (result != VK_SUCCESS) {
return result;
}
monitor_layer_data *my_device_data = GetLayerDataPtr(get_dispatch_key(*pDevice), layer_data_map);
// Setup device dispatch table
my_device_data->device_dispatch_table = new VkuDeviceDispatchTable;
vkuInitDeviceDispatchTable(*pDevice, my_device_data->device_dispatch_table, fpGetDeviceProcAddr);
// store the loader callback for initializing created dispatchable objects
chain_info = get_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK);
if (chain_info) {
my_device_data->pfn_dev_init = chain_info->u.pfnSetDeviceLoaderData;
} else {
my_device_data->pfn_dev_init = NULL;
}
my_device_data->gpu = gpu;
my_device_data->device = *pDevice;
my_device_data->frame = 0;
my_device_data->lastFrame = 0;
my_device_data->fps = 0.0;
time(&my_device_data->lastTime);
// Get our WSI hooks in
VkuDeviceDispatchTable *pTable = my_device_data->device_dispatch_table;
my_device_data->pfnQueuePresentKHR = (PFN_vkQueuePresentKHR)pTable->GetDeviceProcAddr(*pDevice, "vkQueuePresentKHR");
return result;
}
VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices(VkInstance instance, uint32_t *pPhysicalDeviceCount,
VkPhysicalDevice *pPhysicalDevices) {
dispatch_key key = get_dispatch_key(instance);
monitor_layer_data *my_data = GetLayerDataPtr(key, layer_data_map);
VkuInstanceDispatchTable *pTable = my_data->instance_dispatch_table;
VkResult result = pTable->EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices);
if (pPhysicalDevices != nullptr) {
for (int i = 0; i < *pPhysicalDeviceCount; ++i) {
if (layer_instances.count(pPhysicalDevices[i]) == 0) {
layer_instances.insert({pPhysicalDevices[i], instance});
}
}
}
return result;
}
VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t *pPhysicalDeviceGroupCount,
VkPhysicalDeviceGroupProperties *pPhysicalDeviceGroupProperties) {
dispatch_key key = get_dispatch_key(instance);
monitor_layer_data *my_data = GetLayerDataPtr(key, layer_data_map);
VkuInstanceDispatchTable *pTable = my_data->instance_dispatch_table;
VkResult result = pTable->EnumeratePhysicalDeviceGroups(instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroupProperties);
if (pPhysicalDeviceGroupProperties != nullptr) {
for (int i = 0; i < *pPhysicalDeviceGroupCount; ++i) {
for (int j = 0; j < pPhysicalDeviceGroupProperties[i].physicalDeviceCount; ++j) {
if (layer_instances.count(pPhysicalDeviceGroupProperties[i].physicalDevices[j]) == 0) {
layer_instances.insert({pPhysicalDeviceGroupProperties[i].physicalDevices[j], instance});
}
}
}
}
return result;
}
VKAPI_ATTR void VKAPI_CALL vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator) {
dispatch_key key = get_dispatch_key(device);
monitor_layer_data *my_data = GetLayerDataPtr(key, layer_data_map);
VkuDeviceDispatchTable *pTable = my_data->device_dispatch_table;
pTable->DeviceWaitIdle(device);
pTable->DestroyDevice(device, pAllocator);
delete pTable;
layer_data_map.erase(key);
}
VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator,
VkInstance *pInstance) {
#if defined(_WIN32) && defined(_CRTDBG_MODE_FILE)
#if !defined(NDEBUG)
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
#endif
// Avoid "Abort, Retry, Ignore" dialog boxes
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
#endif
VkLayerInstanceCreateInfo *chain_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
assert(chain_info->u.pLayerInfo);
PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
PFN_vkCreateInstance fpCreateInstance = (PFN_vkCreateInstance)fpGetInstanceProcAddr(NULL, "vkCreateInstance");
if (fpCreateInstance == NULL) {
return VK_ERROR_INITIALIZATION_FAILED;
}
// Advance the link info for the next element on the chain
chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance);
if (result != VK_SUCCESS) return result;
monitor_layer_data *my_data = GetLayerDataPtr(get_dispatch_key(*pInstance), layer_data_map);
my_data->instance_dispatch_table = new VkuInstanceDispatchTable;
vkuInitInstanceDispatchTable(*pInstance, my_data->instance_dispatch_table, fpGetInstanceProcAddr);
#if defined(VK_USE_PLATFORM_XCB_KHR)
// Initialize connection to null in case vkCreateXcbSurfaceKHR is never called
my_data->connection = nullptr;
// Load the xcb library and initialize xcb function pointers
if (!xcb.xcbLib) {
xcb.xcbLib = dlopen("libxcb.so", RTLD_NOW | RTLD_LOCAL);
if (xcb.xcbLib) {
xcb.change_property = reinterpret_cast<decltype(xcb_change_property) *>(dlsym(xcb.xcbLib, "xcb_change_property"));
xcb.flush = reinterpret_cast<decltype(xcb_flush) *>(dlsym(xcb.xcbLib, "xcb_flush"));
xcb.get_property = reinterpret_cast<decltype(xcb_get_property) *>(dlsym(xcb.xcbLib, "xcb_get_property"));
xcb.get_property_reply =
reinterpret_cast<decltype(xcb_get_property_reply) *>(dlsym(xcb.xcbLib, "xcb_get_property_reply"));
xcb.get_property_value_length =
reinterpret_cast<decltype(xcb_get_property_value_length) *>(dlsym(xcb.xcbLib, "xcb_get_property_value_length"));
xcb.get_property_value =
reinterpret_cast<decltype(xcb_get_property_value) *>(dlsym(xcb.xcbLib, "xcb_get_property_value"));
if (!xcb.change_property || !xcb.flush || !xcb.get_property || !xcb.get_property_reply ||
!xcb.get_property_value_length || !xcb.get_property_value) {
// Something went wrong querying the entry points - set xcb.xdbLib to NULL
// to indicate we didn't successufly load libxcb.so
xcb.xcbLib = NULL;
}
}
}
#endif
return result;
}
VKAPI_ATTR void VKAPI_CALL vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator) {
dispatch_key key = get_dispatch_key(instance);
monitor_layer_data *my_data = GetLayerDataPtr(key, layer_data_map);
VkuInstanceDispatchTable *pTable = my_data->instance_dispatch_table;
pTable->DestroyInstance(instance, pAllocator);
delete pTable;
layer_data_map.erase(key);
}
VKAPI_ATTR VkResult VKAPI_CALL vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo) {
monitor_layer_data *my_data = GetLayerDataPtr(get_dispatch_key(queue), layer_data_map);
time_t now;
time(&now);
float seconds = (float)difftime(now, my_data->lastTime);
if (seconds > 0.5) {
char str[TITLE_LENGTH + FPS_LENGTH];
char fpsstr[FPS_LENGTH];
monitor_layer_data *my_instance_data = GetLayerDataPtr(get_dispatch_key(my_data->gpu), layer_data_map);
my_data->fps = (my_data->frame - my_data->lastFrame) / seconds;
my_data->lastFrame = my_data->frame;
my_data->lastTime = now;
#if defined(VK_USE_PLATFORM_WIN32_KHR)
if (IsWindow(my_instance_data->hwnd) && !my_instance_data->got_title) {
GetWindowText(my_instance_data->hwnd, my_instance_data->base_title, TITLE_LENGTH);
my_instance_data->got_title = true;
}
#endif
sprintf(fpsstr, " FPS = %.2f", my_data->fps);
strcpy(str, my_instance_data->base_title);
strcat(str, fpsstr);
#if defined(VK_USE_PLATFORM_WIN32_KHR)
if (IsWindow(my_instance_data->hwnd)) {
SetWindowText(my_instance_data->hwnd, str);
}
#elif defined(VK_USE_PLATFORM_XCB_KHR)
if (xcb.xcbLib && my_instance_data->xcb_fps && my_instance_data->connection) {
xcb.change_property(my_instance_data->connection, XCB_PROP_MODE_REPLACE, my_instance_data->xcb_window, XCB_ATOM_WM_NAME,
XCB_ATOM_STRING, 8, strlen(str), str);
xcb.flush(my_instance_data->connection);
}
#endif
}
my_data->frame++;
VkResult result = my_data->pfnQueuePresentKHR(queue, pPresentInfo);
return result;
}
VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceToolPropertiesEXT(VkPhysicalDevice physicalDevice, uint32_t *pToolCount,
VkPhysicalDeviceToolPropertiesEXT *pToolProperties) {
static const VkPhysicalDeviceToolPropertiesEXT monitor_layer_tool_props = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TOOL_PROPERTIES_EXT,
nullptr,
"Monitor Layer",
"1",
VK_TOOL_PURPOSE_PROFILING_BIT_EXT | VK_TOOL_PURPOSE_ADDITIONAL_FEATURES_BIT_EXT,
"The VK_LAYER_LUNARG_monitor utility layer prints the real-time frames-per-second value to the application's title bar.",
"VK_LAYER_LUNARG_monitor"};
auto original_pToolProperties = pToolProperties;
if (pToolProperties != nullptr) {
*pToolProperties = monitor_layer_tool_props;
pToolProperties = ((*pToolCount > 1) ? &pToolProperties[1] : nullptr);
(*pToolCount)--;
}
monitor_layer_data *my_data = GetLayerDataPtr(get_dispatch_key(physicalDevice), layer_data_map);
VkResult result =
my_data->instance_dispatch_table->GetPhysicalDeviceToolPropertiesEXT(physicalDevice, pToolCount, pToolProperties);
if (original_pToolProperties != nullptr) {
pToolProperties = original_pToolProperties;
}
(*pToolCount)++;
return result;
}
#if defined(VK_USE_PLATFORM_WIN32_KHR)
VKAPI_ATTR VkResult VKAPI_CALL vkCreateWin32SurfaceKHR(VkInstance instance, const VkWin32SurfaceCreateInfoKHR *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) {
monitor_layer_data *my_data = GetLayerDataPtr(get_dispatch_key(instance), layer_data_map);
my_data->hwnd = pCreateInfo->hwnd;
VkResult result = my_data->instance_dispatch_table->CreateWin32SurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
return result;
}
#elif defined(VK_USE_PLATFORM_XCB_KHR)
VKAPI_ATTR VkResult VKAPI_CALL vkCreateXcbSurfaceKHR(VkInstance instance, const VkXcbSurfaceCreateInfoKHR *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) {
static bool xcbErrorPrinted = false; // Only print xcb error message once
xcb_get_property_cookie_t cookie;
xcb_get_property_reply_t *reply;
xcb_atom_t property = XCB_ATOM_WM_NAME;
xcb_atom_t type = XCB_ATOM_STRING;
monitor_layer_data *my_data = GetLayerDataPtr(get_dispatch_key(instance), layer_data_map);
if (!xcb.xcbLib and !xcbErrorPrinted) {
fprintf(stderr, "Monitor layer libxcb.so load failure, will not be able to display frame rate\n");
xcbErrorPrinted = true;
}
if (xcb.xcbLib) {
my_data->xcb_window = pCreateInfo->window;
my_data->connection = pCreateInfo->connection;
cookie = xcb.get_property(my_data->connection, 0, my_data->xcb_window, property, type, 0, 0);
if ((reply = xcb.get_property_reply(my_data->connection, cookie, NULL))) {
my_data->xcb_fps = true;
int len = xcb.get_property_value_length(reply);
if (len > TITLE_LENGTH) {
my_data->xcb_fps = false;
} else if (len > 0) {
strcpy(my_data->base_title, (char *)xcb.get_property_value(reply));
} else {
// No window title - make base title null string
my_data->base_title[0] = 0;
}
}
}
VkResult result = my_data->instance_dispatch_table->CreateXcbSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
return result;
}
#endif
#if defined(__GNUC__) && __GNUC__ >= 4
#define EXPORT_FUNCTION __attribute__((visibility("default")))
#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)
#define EXPORT_FUNCTION __attribute__((visibility("default")))
#else
#define EXPORT_FUNCTION
#endif
EXPORT_FUNCTION VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice dev, const char *funcName) {
#define ADD_HOOK(fn) \
if (!strncmp(#fn, funcName, sizeof(#fn))) return (PFN_vkVoidFunction)fn
ADD_HOOK(vkGetDeviceProcAddr);
ADD_HOOK(vkDestroyDevice);
ADD_HOOK(vkQueuePresentKHR);
#undef ADD_HOOK
if (dev == NULL) return NULL;
monitor_layer_data *dev_data;
dev_data = GetLayerDataPtr(get_dispatch_key(dev), layer_data_map);
VkuDeviceDispatchTable *pTable = dev_data->device_dispatch_table;
if (pTable->GetDeviceProcAddr == NULL) return NULL;
return pTable->GetDeviceProcAddr(dev, funcName);
}
EXPORT_FUNCTION VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char *funcName) {
#define ADD_HOOK(fn) \
if (!strncmp(#fn, funcName, sizeof(#fn))) return (PFN_vkVoidFunction)fn
ADD_HOOK(vkCreateInstance);
ADD_HOOK(vkEnumeratePhysicalDevices);
ADD_HOOK(vkEnumeratePhysicalDeviceGroups);
ADD_HOOK(vkCreateDevice);
ADD_HOOK(vkDestroyInstance);
ADD_HOOK(vkGetInstanceProcAddr);
ADD_HOOK(vkGetPhysicalDeviceToolPropertiesEXT);
#if defined(VK_USE_PLATFORM_WIN32_KHR)
ADD_HOOK(vkCreateWin32SurfaceKHR);
#elif defined(VK_USE_PLATFORM_XCB_KHR)
ADD_HOOK(vkCreateXcbSurfaceKHR);
#endif
#undef ADD_HOOK
if (instance == NULL) return NULL;
monitor_layer_data *instance_data;
instance_data = GetLayerDataPtr(get_dispatch_key(instance), layer_data_map);
VkuInstanceDispatchTable *pTable = instance_data->instance_dispatch_table;
if (pTable->GetInstanceProcAddr == NULL) return NULL;
return pTable->GetInstanceProcAddr(instance, funcName);
}