Last active
March 31, 2026 06:50
-
-
Save YellowOnion/cbf40c3c8f848de45878cd575d79acec to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdio.h> | |
| #include <string.h> | |
| #include <stdbool.h> | |
| #include <vulkan/vulkan.h> | |
| #include <vulkan/vk_layer.h> | |
| #include <assert.h> | |
| #include <glib.h> | |
| #include <time.h> | |
| #include "kairos.h" | |
| const char* _LAYER_PREFIX = "VK_LAYER_KAIROS"; | |
| const char* _LAYER_NAME = "VK_LAYER_KAIROS_Dynamic"; | |
| enum LOG_LEVEL { | |
| LOG_TRACE, | |
| LOG_DEBUG, | |
| LOG_MSG, | |
| LOG_ERROR | |
| }; | |
| FILE *__log_file_fd = NULL; | |
| const int log_level = LOG_TRACE; | |
| #define LOG_MSG(level, ...) ({ \ | |
| if (level >= log_level) { \ | |
| fprintf(__log_file_fd, __VA_ARGS__); \ | |
| fflush(__log_file_fd); \ | |
| } \ | |
| }) | |
| typedef struct { | |
| int64_t t_cpu; | |
| int64_t t_gpu; | |
| int64_t t_idle; | |
| int64_t t_slew; | |
| int64_t t_t3; | |
| } time_stats_t; | |
| time_stats_t __time_stats = {0, 0, 0, 0, 0}; | |
| int64_t gettime() { | |
| struct timespec ts; | |
| clock_gettime(CLOCK_MONOTONIC, &ts); | |
| return (int64_t)ts.tv_sec * 1000 * 1000 * 1000 + (int64_t)ts.tv_nsec; | |
| } | |
| #define GET_KEY(name) (*(void **)name) | |
| #define NON_OVERRIDES() \ | |
| X(CreateInstance) \ | |
| X(GetInstanceProcAddr) \ | |
| X(GetDeviceProcAddr) | |
| #define INSTANCE_OVERRIDES() \ | |
| X(DestroyInstance) \ | |
| X(CreateDevice) \ | |
| X(DestroyDevice) \ | |
| X(EnumerateInstanceLayerProperties) \ | |
| X(EnumerateInstanceExtensionProperties) \ | |
| X(EnumerateDeviceExtensionProperties) | |
| #define DEVICE_OVERRIDES() \ | |
| X(CreateDevice) \ | |
| X(DestroyDevice) \ | |
| X(QueuePresentKHR) | |
| enum INSTANCE_CMD { | |
| #define X(name) name, | |
| INSTANCE_OVERRIDES | |
| #undef X | |
| }; | |
| typedef struct instance_override_t { | |
| PFN_vkGetInstanceProcAddr GetInstanceProcAddr; | |
| #define X(name) PFN_vk##name name; | |
| INSTANCE_OVERRIDES() | |
| #undef X | |
| } instance_override_t; | |
| GRWLock instance_lock; | |
| GHashTable *instance_overrides = NULL; | |
| typedef struct device_override_t { | |
| PFN_vkGetDeviceProcAddr GetDeviceProcAddr; | |
| #define X(name) PFN_vk##name name; | |
| DEVICE_OVERRIDES() | |
| #undef X | |
| } device_override_t; | |
| GRWLock device_lock; | |
| GHashTable *device_overrides = NULL; | |
| __attribute__((constructor)) | |
| static void __Kairos_construct() { | |
| __log_file_fd = fopen("kairos.log", "w+"); | |
| g_rw_lock_init(&instance_lock); | |
| instance_overrides = g_hash_table_new(&g_direct_hash, &g_direct_equal); | |
| g_rw_lock_init(&device_lock); | |
| device_overrides = g_hash_table_new(&g_direct_hash, &g_direct_equal); | |
| } | |
| __attribute__((destructor)) | |
| static void __Kairos_destruct() { | |
| fclose(__log_file_fd); | |
| g_hash_table_destroy(instance_overrides); | |
| g_hash_table_destroy(device_overrides); | |
| } | |
| extern VkResult VKAPI_CALL Kairos_CreateInstance( | |
| const VkInstanceCreateInfo* pCreateInfo, | |
| const VkAllocationCallbacks* pAllocator, | |
| VkInstance* pInstance) { | |
| LOG_MSG(LOG_TRACE, "CreateInstance\n"); | |
| VkLayerInstanceCreateInfo *layerCreateInfo = | |
| (VkLayerInstanceCreateInfo *)pCreateInfo->pNext; | |
| while (layerCreateInfo && | |
| !(layerCreateInfo->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO | |
| && layerCreateInfo->function == VK_LAYER_LINK_INFO)) { | |
| layerCreateInfo = (VkLayerInstanceCreateInfo *)layerCreateInfo->pNext; | |
| } | |
| assert(layerCreateInfo != NULL); | |
| PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = | |
| layerCreateInfo->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 of the chain. | |
| // This ensures that the next layer gets it's layer info and not | |
| // the info for our current layer. | |
| layerCreateInfo->u.pLayerInfo = layerCreateInfo->u.pLayerInfo->pNext; | |
| // Continue call down the chain | |
| VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance); | |
| if (result != VK_SUCCESS) | |
| return result; | |
| instance_override_t *instance_override = g_new0(instance_override_t, 1); | |
| instance_override->GetInstanceProcAddr = fpGetInstanceProcAddr; | |
| #define X(cmdname) \ | |
| instance_override->cmdname = (PFN_vk##cmdname)fpGetInstanceProcAddr(*pInstance, "vk"#cmdname); | |
| INSTANCE_OVERRIDES() | |
| #undef X | |
| g_rw_lock_writer_lock(&instance_lock); | |
| g_hash_table_insert(instance_overrides, GET_KEY(*pInstance), | |
| instance_override); | |
| g_rw_lock_writer_unlock(&instance_lock); | |
| __time_stats.t_t3 = gettime(); | |
| return VK_SUCCESS; | |
| } | |
| extern void VKAPI_CALL Kairos_DestroyInstance( | |
| VkInstance pInstance, | |
| VkAllocationCallbacks *pAllocator) { | |
| LOG_MSG(LOG_TRACE, "DestroyInstance\n"); | |
| instance_override_t *instance_override = NULL; | |
| g_rw_lock_writer_lock(&instance_lock); | |
| g_hash_table_lookup(instance_overrides, instance_override); | |
| g_free(instance_override); | |
| g_hash_table_remove(instance_overrides, GET_KEY(pInstance)); | |
| g_rw_lock_writer_unlock(&instance_lock); | |
| } | |
| extern PFN_vkVoidFunction Kairos_GetInstanceProcAddr( | |
| VkInstance pInstance, | |
| const char* name) { | |
| LOG_MSG(LOG_TRACE, "GIPA: %s\n", name); | |
| #define X(cmdname) \ | |
| if (!strcmp("vk" #cmdname, name)) return (PFN_vkVoidFunction)&Kairos_##cmdname;\ | |
| NON_OVERRIDES() | |
| INSTANCE_OVERRIDES() | |
| DEVICE_OVERRIDES() | |
| #undef X | |
| g_rw_lock_reader_lock(&instance_lock); | |
| instance_override_t *ret = g_hash_table_lookup(instance_overrides, GET_KEY(pInstance)); | |
| g_rw_lock_reader_unlock(&instance_lock); | |
| assert(ret != 0); | |
| return ret->GetInstanceProcAddr(pInstance, name); | |
| } | |
| extern VkResult VKAPI_CALL Kairos_CreateDevice( | |
| VkPhysicalDevice physicalDevice, | |
| const VkDeviceCreateInfo* pCreateInfo, | |
| const VkAllocationCallbacks* pAllocator, | |
| VkDevice* pDevice) { | |
| LOG_MSG(LOG_TRACE, "CreateDevice\n"); | |
| VkLayerDeviceCreateInfo *layerCreateInfo = | |
| (VkLayerDeviceCreateInfo *)pCreateInfo->pNext; | |
| // step through the chain of pNext until we get to the link info | |
| while (layerCreateInfo && | |
| !(layerCreateInfo->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO | |
| && layerCreateInfo->function == VK_LAYER_LINK_INFO)) { | |
| layerCreateInfo = (VkLayerDeviceCreateInfo *)layerCreateInfo->pNext; | |
| } | |
| if (layerCreateInfo == NULL) { | |
| LOG_MSG(LOG_TRACE, "layerCreateInfo is NULL\n"); | |
| return VK_ERROR_INITIALIZATION_FAILED; | |
| } | |
| PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr | |
| = layerCreateInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr; | |
| PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = | |
| layerCreateInfo->u.pLayerInfo->pfnNextGetDeviceProcAddr; | |
| // move chain on for next layer | |
| layerCreateInfo->u.pLayerInfo = layerCreateInfo->u.pLayerInfo->pNext; | |
| PFN_vkCreateDevice fpCreateDevice = | |
| (PFN_vkCreateDevice)fpGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateDevice"); | |
| VkResult ret = fpCreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice); | |
| if (ret != VK_SUCCESS) { | |
| LOG_MSG(LOG_TRACE, "fpCreateDevice failed ret: %d", ret); | |
| return ret; | |
| } | |
| device_override_t *device_override = g_new0(device_override_t, 1); | |
| device_override->GetDeviceProcAddr = fpGetDeviceProcAddr; | |
| #define X(cmdname) \ | |
| device_override->cmdname = (PFN_vk##cmdname)fpGetDeviceProcAddr(*pDevice, "vk"#cmdname);\ | |
| DEVICE_OVERRIDES() | |
| #undef X | |
| g_rw_lock_writer_lock(&device_lock); | |
| g_hash_table_insert(device_overrides, GET_KEY(*pDevice), | |
| device_override); | |
| g_rw_lock_writer_unlock(&device_lock); | |
| return VK_SUCCESS; | |
| } | |
| extern void VKAPI_CALL Kairos_DestroyDevice( | |
| VkDevice pDevice, | |
| const VkAllocationCallbacks* pAllocator) { | |
| LOG_MSG(LOG_TRACE, "DestroyDevice\n"); | |
| device_override_t *device_override = NULL; | |
| g_rw_lock_writer_lock(&device_lock); | |
| g_hash_table_lookup(device_overrides, device_override); | |
| g_free(device_override); | |
| g_hash_table_remove(device_overrides, GET_KEY(pDevice)); | |
| g_rw_lock_writer_unlock(&device_lock); | |
| } | |
| extern PFN_vkVoidFunction Kairos_GetDeviceProcAddr( | |
| VkDevice pDevice, | |
| const char* name) { | |
| LOG_MSG(LOG_TRACE, "GDPA: %s\n", name); | |
| #define X(cmdname) \ | |
| if (!strcmp("vk" #cmdname, name)) return (PFN_vkVoidFunction)&Kairos_##cmdname; \ | |
| NON_OVERRIDES() | |
| DEVICE_OVERRIDES() | |
| #undef X | |
| if (pDevice == NULL) return NULL; | |
| g_rw_lock_reader_lock(&device_lock); | |
| device_override_t *ret = g_hash_table_lookup(device_overrides, GET_KEY(pDevice)); | |
| g_rw_lock_reader_unlock(&device_lock); | |
| assert(ret != 0); | |
| return ret->GetDeviceProcAddr(pDevice, name); | |
| } | |
| extern VkResult VKAPI_CALL Kairos_EnumerateInstanceLayerProperties( | |
| uint32_t *pPropertyCount, | |
| VkLayerProperties *pProperties) { | |
| LOG_MSG(LOG_TRACE, "EnumerateInstanceLayersProperties: start\n"); | |
| if(pPropertyCount) *pPropertyCount = 1; | |
| if(pProperties) { | |
| strcpy(pProperties->layerName, _LAYER_NAME); | |
| strcpy(pProperties->description, "a dynamic framerate control"); | |
| pProperties->implementationVersion = 1; | |
| pProperties->specVersion = VK_API_VERSION_1_0; | |
| } | |
| return VK_SUCCESS; | |
| } | |
| extern VkResult VKAPI_CALL Kairos_EnumerateInstanceExtensionProperties( | |
| const char *pLayerName, | |
| uint32_t *pPropertyCount, | |
| VkExtensionProperties *pProperties) { | |
| LOG_MSG(LOG_TRACE, "EnumerateInstanceExtensionProperties: %s\n", pLayerName); | |
| if(pLayerName == NULL || strcmp(pLayerName, _LAYER_NAME)) | |
| return VK_ERROR_LAYER_NOT_PRESENT; | |
| // don't expose any extensions | |
| if(pPropertyCount) *pPropertyCount = 0; | |
| return VK_SUCCESS; | |
| } | |
| extern VkResult VKAPI_CALL Kairos_EnumerateDeviceExtensionProperties( | |
| VkPhysicalDevice physicalDevice, | |
| const char *pLayerName, | |
| uint32_t *pPropertyCount, | |
| VkExtensionProperties *pProperties) { | |
| LOG_MSG(LOG_TRACE, "EnumerateInstanceExtensionProperties: %s\n", pLayerName); | |
| // pass through any queries that aren't to us | |
| if(pLayerName == NULL || strcmp(pLayerName, _LAYER_NAME)) { | |
| if(physicalDevice == VK_NULL_HANDLE) | |
| return VK_SUCCESS; | |
| g_rw_lock_reader_lock(&device_lock); | |
| instance_override_t *ret = g_hash_table_lookup(instance_overrides, GET_KEY(physicalDevice)); | |
| g_rw_lock_reader_unlock(&device_lock); | |
| assert(ret != 0); | |
| return ret->EnumerateDeviceExtensionProperties( | |
| physicalDevice, pLayerName, pPropertyCount, pProperties); | |
| } | |
| // don't expose any extensions | |
| if(pPropertyCount) *pPropertyCount = 0; | |
| return VK_SUCCESS; | |
| } | |
| void print_time(const char * name, int64_t time, const bool nl) { | |
| const int64_t ms = div(time / 1000, 1000).quot; | |
| const int64_t us = div(time / 1000, 1000).rem; | |
| const char *sign = us < 0 && ms < 0 ? "-" : "+"; | |
| fprintf(__log_file_fd, | |
| "%s: %s%ld.%03ld ms%s", | |
| name, | |
| sign, | |
| ABS(ms), | |
| ABS(us), | |
| nl ? "\n": " "); | |
| } | |
| extern VkResult VKAPI_CALL Kairos_QueuePresentKHR( | |
| VkQueue queue, | |
| VkPresentInfoKHR *pPresentInfo) { | |
| g_rw_lock_reader_lock(&device_lock); | |
| device_override_t *ret = g_hash_table_lookup(device_overrides, GET_KEY(queue)); | |
| g_rw_lock_reader_unlock(&device_lock); | |
| assert(ret != 0); | |
| PFN_vkQueuePresentKHR fpQueuePresentKHR = ret->QueuePresentKHR; | |
| time_stats_t old_time_stats = __time_stats; | |
| int64_t t1 = gettime(); | |
| VkResult qp_ret = fpQueuePresentKHR(queue, pPresentInfo); | |
| int64_t t2 = gettime(); | |
| int64_t cpu = (t1 - old_time_stats.t_t3 + old_time_stats.t_cpu) / 2; | |
| int64_t gpu = (t2 - t1 + old_time_stats.t_gpu) / 2; | |
| int64_t max = 20 * 1000 * 1000; | |
| int64_t min = 0; | |
| int64_t dyn = 95 * (old_time_stats.t_idle + gpu - cpu) / 100; | |
| int64_t delay = MIN(max, MAX(dyn, min)); | |
| print_time("delay", delay, false); | |
| print_time("cpu", cpu, false); | |
| print_time("gpu", gpu, false); | |
| print_time("idle", old_time_stats.t_idle, false); | |
| print_time("slew", old_time_stats.t_slew, true); | |
| assert (ABS(delay) < (uint64_t)10 * 1000 * 1000 * 1000); | |
| if (delay > 0) { | |
| const div_t d = div(delay, 1000 * 1000 * 1000); | |
| struct timespec ts = {d.quot, d.rem}; | |
| //struct timespec rem = {0,0}; | |
| clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); | |
| } | |
| int64_t t3 = gettime(); | |
| int64_t idle = t3 - t2; | |
| int64_t slew = ((delay > 0 ? idle - delay : 0 ) + old_time_stats.t_slew) / 2 ; | |
| __time_stats.t_cpu = cpu; | |
| __time_stats.t_gpu = gpu; | |
| __time_stats.t_idle = idle; | |
| __time_stats.t_slew = slew; | |
| __time_stats.t_t3 = t3; | |
| return qp_ret; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #ifndef KAIROS_H_ | |
| #define KAIROS_H_ | |
| #include <vulkan/vulkan.h> | |
| extern void VKAPI_CALL Kairos_DestroyInstance( | |
| VkInstance pInstance, | |
| VkAllocationCallbacks *pAllocator); | |
| extern PFN_vkVoidFunction Kairos_GetDeviceProcAddr( | |
| VkDevice pDevice, | |
| const char* name); | |
| extern VkResult VKAPI_CALL Kairos_CreateDevice( | |
| VkPhysicalDevice physicalDevice, | |
| const VkDeviceCreateInfo* pCreateInfo, | |
| const VkAllocationCallbacks* pAllocator, | |
| VkDevice* pDevice); | |
| extern void VKAPI_CALL Kairos_DestroyDevice( | |
| VkDevice pDevice, | |
| const VkAllocationCallbacks* pAllocator); | |
| extern VkResult VKAPI_CALL Kairos_EnumerateInstanceLayerProperties( | |
| uint32_t *pPropertyCount, | |
| VkLayerProperties *pProperties); | |
| extern VkResult VKAPI_CALL Kairos_EnumerateDeviceLayerProperties( | |
| VkPhysicalDevice physicalDevice, | |
| uint32_t *pPropertyCount, | |
| VkLayerProperties *pProperties); | |
| extern VkResult VKAPI_CALL Kairos_EnumerateInstanceExtensionProperties( | |
| const char *pLayerName, | |
| uint32_t *pPropertyCount, | |
| VkExtensionProperties *pProperties); | |
| extern VkResult VKAPI_CALL Kairos_EnumerateDeviceExtensionProperties( | |
| VkPhysicalDevice physicalDevice, | |
| const char *pLayerName, | |
| uint32_t *pPropertyCount, | |
| VkExtensionProperties *pProperties); | |
| extern VkResult VKAPI_CALL Kairos_QueuePresentKHR( | |
| VkQueue pQueue, | |
| VkPresentInfoKHR *pPresentInfo); | |
| #endif // KAIROS_H_ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "file_format_version" : "1.0.0", | |
| "layer" : { | |
| "name": "VK_LAYER_KAIROS_Dynamic", | |
| "type": "GLOBAL", | |
| "library_path": "/home/daniel/dev/kairos/build/libkairos.so", | |
| "api_version": "1.4.0", | |
| "implementation_version": "1", | |
| "description": "Dynamic Framerate control", | |
| "functions": { | |
| "vkGetInstanceProcAddr": "Kairos_GetInstanceProcAddr", | |
| "vkGetDeviceProcAddr": "Kairos_GetDeviceProcAddr" | |
| }, | |
| "enable_environment": { | |
| "ENABLE_KAIROS_LAYER": "1" | |
| }, | |
| "disable_environment": { | |
| "DISABLE_KAIROS_LAYER": "1" | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment