diff --git a/include/hooks.hpp b/include/hooks.hpp index 487686e..e38212c 100644 --- a/include/hooks.hpp +++ b/include/hooks.hpp @@ -14,7 +14,6 @@ namespace Hooks { VkDevice device; VkPhysicalDevice physicalDevice; std::pair queue; // graphics family - uint64_t frameGen; // amount of frames to generate }; /// Map of hooked Vulkan functions. diff --git a/include/utils/utils.hpp b/include/utils/utils.hpp index 138eb00..27b8264 100644 --- a/include/utils/utils.hpp +++ b/include/utils/utils.hpp @@ -4,8 +4,9 @@ #include #include -#include #include +#include +#include namespace Utils { @@ -68,4 +69,20 @@ namespace Utils { VkPipelineStageFlags pre, VkPipelineStageFlags post, bool makeSrcPresentable, bool makeDstPresentable); + /// + /// Log a message at most n times. + /// + /// @param id The identifier for the log message. + /// @param n The maximum number of times to log the message. + /// @param message The message to log. + /// + void logLimitN(const std::string& id, size_t n, const std::string& message); + + /// + /// Reset the log limit for a given identifier. + /// + /// @param id The identifier for the log message. + /// + void resetLimitN(const std::string& id) noexcept; + } diff --git a/src/hooks.cpp b/src/hooks.cpp index c589b79..6393aa3 100644 --- a/src/hooks.cpp +++ b/src/hooks.cpp @@ -1,248 +1,227 @@ #include "hooks.hpp" +#include "common/exception.hpp" +#include "utils/utils.hpp" #include "context.hpp" #include "layer.hpp" -#include "utils/log.hpp" -#include "utils/utils.hpp" -#include "common/exception.hpp" #include -#include -#include +#include +#include #include #include +#include +#include #include -#include #include using namespace Hooks; namespace { - // instance hooks - + /// + /// Add extensions to the instance create info. + /// VkResult myvkCreateInstance( const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance) { - // add extensions - auto extensions = Utils::addExtensions(pCreateInfo->ppEnabledExtensionNames, - pCreateInfo->enabledExtensionCount, { + auto extensions = Utils::addExtensions( + pCreateInfo->ppEnabledExtensionNames, + pCreateInfo->enabledExtensionCount, + { "VK_KHR_get_physical_device_properties2", "VK_KHR_external_memory_capabilities", "VK_KHR_external_semaphore_capabilities" - }); + } + ); VkInstanceCreateInfo createInfo = *pCreateInfo; createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); auto res = Layer::ovkCreateInstance(&createInfo, pAllocator, pInstance); - if (res != VK_SUCCESS) { - Log::error("hooks", "Failed to create Vulkan instance: {:x}", - static_cast(res)); - return res; - } - - Log::info("hooks", "Instance created successfully: {:x}", - reinterpret_cast(*pInstance)); + if (res == VK_ERROR_EXTENSION_NOT_PRESENT) + throw std::runtime_error( + "Required Vulkan instance extensions are not present." + "Your GPU driver is not supported."); return res; } - void myvkDestroyInstance( - VkInstance instance, - const VkAllocationCallbacks* pAllocator) { - Log::info("hooks", "Instance destroyed successfully: {:x}", - reinterpret_cast(instance)); - Layer::ovkDestroyInstance(instance, pAllocator); - } - - // device hooks - - std::unordered_map devices; + /// Map of devices to related information. + std::unordered_map deviceToInfo; + /// + /// Add extensions to the device create info. + /// (function pointers are not initialized yet) + /// VkResult myvkCreateDevicePre( VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) { // add extensions - auto extensions = Utils::addExtensions(pCreateInfo->ppEnabledExtensionNames, - pCreateInfo->enabledExtensionCount, { + auto extensions = Utils::addExtensions( + pCreateInfo->ppEnabledExtensionNames, + pCreateInfo->enabledExtensionCount, + { "VK_KHR_external_memory", "VK_KHR_external_memory_fd", "VK_KHR_external_semaphore", "VK_KHR_external_semaphore_fd" - }); - + } + ); VkDeviceCreateInfo createInfo = *pCreateInfo; createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); auto res = Layer::ovkCreateDevice(physicalDevice, &createInfo, pAllocator, pDevice); - if (res != VK_SUCCESS) { - Log::error("hooks", "Failed to create Vulkan device: {:x}", - static_cast(res)); - return res; - } - - Log::info("hooks", "Device created successfully: {:x}", - reinterpret_cast(*pDevice)); + if (res == VK_ERROR_EXTENSION_NOT_PRESENT) + throw std::runtime_error( + "Required Vulkan device extensions are not present." + "Your GPU driver is not supported."); return res; } + /// + /// Add related device information after the device is created. + /// VkResult myvkCreateDevicePost( VkPhysicalDevice physicalDevice, VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks*, VkDevice* pDevice) { - // store device info - Log::debug("hooks", "Creating device info for device: {:x}", - reinterpret_cast(*pDevice)); - try { - const char* frameGenEnv = std::getenv("LSFG_MULTIPLIER"); - const uint64_t frameGen = static_cast( - std::max(1, std::stol(frameGenEnv ? frameGenEnv : "2") - 1)); - Log::debug("hooks", "Using {}x frame generation", - frameGen + 1); - - auto queue = Utils::findQueue(*pDevice, physicalDevice, pCreateInfo, - VK_QUEUE_GRAPHICS_BIT); - Log::debug("hooks", "Found queue at index {}: {:x}", - queue.first, reinterpret_cast(queue.second)); - - devices.emplace(*pDevice, DeviceInfo { - .device = *pDevice, - .physicalDevice = physicalDevice, - .queue = queue, - .frameGen = frameGen, - }); - } catch (const std::exception& e) { - Log::error("hooks", "Failed to create device info: {}", e.what()); - return VK_ERROR_INITIALIZATION_FAILED; - } - - Log::info("hooks", "Device info created successfully for: {:x}", - reinterpret_cast(*pDevice)); + deviceToInfo.emplace(*pDevice, DeviceInfo { + .device = *pDevice, + .physicalDevice = physicalDevice, + .queue = Utils::findQueue(*pDevice, physicalDevice, pCreateInfo, VK_QUEUE_GRAPHICS_BIT) + }); return VK_SUCCESS; } - void myvkDestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) { - devices.erase(device); // erase device info - - Log::info("hooks", "Device & Device info destroyed successfully: {:x}", - reinterpret_cast(device)); + /// Erase the device information when the device is destroyed. + void myvkDestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) noexcept { + deviceToInfo.erase(device); Layer::ovkDestroyDevice(device, pAllocator); } - // swapchain hooks - std::unordered_map swapchains; std::unordered_map swapchainToDeviceTable; + /// + /// Adjust swapchain creation parameters and create a swapchain context. + /// VkResult myvkCreateSwapchainKHR( VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, - VkSwapchainKHR* pSwapchain) { - auto it = devices.find(device); - if (it == devices.end()) { - Log::warn("hooks", "Created swapchain without device info present"); + VkSwapchainKHR* pSwapchain) noexcept { + // find device + auto it = deviceToInfo.find(device); + if (it == deviceToInfo.end()) { + Utils::logLimitN("swapMap", 5, "Device not found in map"); return Layer::ovkCreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain); } + Utils::resetLimitN("swapMap"); auto& deviceInfo = it->second; - // update swapchain create info + // increase amount of images in swapchain VkSwapchainCreateInfoKHR createInfo = *pCreateInfo; - const uint32_t maxImageCount = Utils::getMaxImageCount(deviceInfo.physicalDevice, pCreateInfo->surface); - const uint32_t imageCount = createInfo.minImageCount + 1 + static_cast(deviceInfo.frameGen); - Log::debug("hooks", "Creating swapchain with max image count: {}/{}", imageCount, maxImageCount); - if (imageCount > maxImageCount) { - Log::warn("hooks", "LSFG_MULTIPLIER is set very high. This might lead to performance degradation"); - createInfo.minImageCount = maxImageCount; // limit to max possible + const auto maxImages = Utils::getMaxImageCount( + deviceInfo.physicalDevice, pCreateInfo->surface); + createInfo.minImageCount = createInfo.minImageCount + 1 + + static_cast(deviceInfo.queue.first); + if (createInfo.minImageCount > maxImages) { + createInfo.minImageCount = maxImages; + Utils::logLimitN("swapCount", 10, + "Requested image count (" + + std::to_string(pCreateInfo->minImageCount) + ") " + "exceeds maximum allowed (" + + std::to_string(maxImages) + "). " + "Continuing with maximum allowed image count. " + "This might lead to performance degradation."); } else { - createInfo.minImageCount = imageCount; // set to frameGen + 1 + Utils::resetLimitN("swapCount"); } - createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; // allow copy from/to images - createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; // force vsync - auto res = Layer::ovkCreateSwapchainKHR(device, &createInfo, pAllocator, pSwapchain); - if (res != VK_SUCCESS) { - Log::error("hooks", "Failed to create swapchain: {}", static_cast(res)); - return res; - } - Log::info("hooks", "Swapchain created successfully: {:x}", - reinterpret_cast(*pSwapchain)); - // retire previous swapchain if it exists + // allow copy operations on swapchain images + createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + + // enforce vsync + createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; + + // retire potential old swapchain if (pCreateInfo->oldSwapchain) { - Log::debug("hooks", "Retiring previous swapchain context: {:x}", - reinterpret_cast(pCreateInfo->oldSwapchain)); swapchains.erase(pCreateInfo->oldSwapchain); swapchainToDeviceTable.erase(pCreateInfo->oldSwapchain); - Log::info("hooks", "Previous swapchain context retired successfully: {:x}", - reinterpret_cast(pCreateInfo->oldSwapchain)); } - // create swapchain context - Log::debug("hooks", "Creating swapchain context for device: {:x}", - reinterpret_cast(device)); + // create swapchain + auto res = Layer::ovkCreateSwapchainKHR(device, &createInfo, pAllocator, pSwapchain); + if (res != VK_SUCCESS) + return res; // can't be caused by lsfg-vk (yet) + try { - // get swapchain images + // get all swapchain images uint32_t imageCount{}; res = Layer::ovkGetSwapchainImagesKHR(device, *pSwapchain, &imageCount, nullptr); if (res != VK_SUCCESS || imageCount == 0) - throw LSFG::vulkan_error(res, "Failed to get swapchain images count"); + throw LSFG::vulkan_error(res, "Failed to get swapchain image count"); std::vector swapchainImages(imageCount); - res = Layer::ovkGetSwapchainImagesKHR(device, *pSwapchain, &imageCount, swapchainImages.data()); + res = Layer::ovkGetSwapchainImagesKHR(device, *pSwapchain, + &imageCount, swapchainImages.data()); if (res != VK_SUCCESS) throw LSFG::vulkan_error(res, "Failed to get swapchain images"); - Log::debug("hooks", "Swapchain has {} images", swapchainImages.size()); // create swapchain context + swapchainToDeviceTable.emplace(*pSwapchain, device); swapchains.emplace(*pSwapchain, LsContext( deviceInfo, *pSwapchain, pCreateInfo->imageExtent, swapchainImages )); - swapchainToDeviceTable.emplace(*pSwapchain, device); - } catch (const LSFG::vulkan_error& e) { - Log::error("hooks", "Encountered Vulkan error {:x} while creating swapchain context: {}", - static_cast(e.error()), e.what()); - return e.error(); + + Utils::resetLimitN("swapCtxCreate"); } catch (const std::exception& e) { - Log::error("hooks", "Encountered error while creating swapchain context: {}", e.what()); + Utils::logLimitN("swapCtxCreate", 5, + "An error occurred while creating the swapchain wrapper:\n" + "- " + std::string(e.what())); return VK_ERROR_INITIALIZATION_FAILED; } - - Log::info("hooks", "Swapchain context created successfully for: {:x}", - reinterpret_cast(*pSwapchain)); - return res; + return VK_SUCCESS; } + /// + /// Update presentation parameters and present the next frame(s). + /// VkResult myvkQueuePresentKHR( VkQueue queue, - const VkPresentInfoKHR* pPresentInfo) { + const VkPresentInfoKHR* pPresentInfo) noexcept { + // find swapchain device auto it = swapchainToDeviceTable.find(*pPresentInfo->pSwapchains); if (it == swapchainToDeviceTable.end()) { - Log::warn("hooks2", "Swapchain {:x} not found in swapchainToDeviceTable", - reinterpret_cast(*pPresentInfo->pSwapchains)); + Utils::logLimitN("swapMap", 5, + "Swapchain not found in map"); return Layer::ovkQueuePresentKHR(queue, pPresentInfo); } - auto it2 = devices.find(it->second); - if (it2 == devices.end()) { - Log::warn("hooks2", "Device {:x} not found in devices", - reinterpret_cast(it->second)); + + // find device info + auto it2 = deviceToInfo.find(it->second); + if (it2 == deviceToInfo.end()) { + Utils::logLimitN("swapMap", 5, + "Device not found in map"); return Layer::ovkQueuePresentKHR(queue, pPresentInfo); } + auto& deviceInfo = it2->second; + + // find swapchain context auto it3 = swapchains.find(*pPresentInfo->pSwapchains); if (it3 == swapchains.end()) { - Log::warn("hooks2", "Swapchain {:x} not found in swapchains", - reinterpret_cast(*pPresentInfo->pSwapchains)); + Utils::logLimitN("swapMap", 5, + "Swapchain context not found in map"); return Layer::ovkQueuePresentKHR(queue, pPresentInfo); } - - auto& deviceInfo = it2->second; auto& swapchain = it3->second; - // patch vsync NOLINTBEGIN + // enforce vsync | NOLINTBEGIN #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" const VkSwapchainPresentModeInfoEXT* presentModeInfo = @@ -255,48 +234,35 @@ namespace { } presentModeInfo = reinterpret_cast(presentModeInfo->pNext); - } // NOLINTEND -#pragma clang diagnostic pop + } + #pragma clang diagnostic pop - // present the next frame - Log::debug("hooks2", "Presenting swapchain: {:x} on queue: {:x}", - reinterpret_cast(*pPresentInfo->pSwapchains), - reinterpret_cast(queue)); - VkResult res{}; + // NOLINTEND | present the next frame + VkResult res{}; // might return VK_SUBOPTIMAL_KHR try { std::vector semaphores(pPresentInfo->waitSemaphoreCount); std::copy_n(pPresentInfo->pWaitSemaphores, semaphores.size(), semaphores.data()); - Log::debug("hooks2", "Waiting on {} semaphores", semaphores.size()); - // present the next frame res = swapchain.present(deviceInfo, pPresentInfo->pNext, queue, semaphores, *pPresentInfo->pImageIndices); - } catch (const LSFG::vulkan_error& e) { - Log::error("hooks2", "Encountered Vulkan error {:x} while presenting: {}", - static_cast(e.error()), e.what()); - return e.error(); + + Utils::resetLimitN("swapPresent"); } catch (const std::exception& e) { - Log::error("hooks2", "Encountered error while creating presenting: {}", - e.what()); + Utils::logLimitN("swapPresent", 5, + "An error occurred while presenting the swapchain:\n" + "- " + std::string(e.what())); return VK_ERROR_INITIALIZATION_FAILED; } - - // non VK_SUCCESS or VK_SUBOPTIMAL_KHR doesn't reach here - Log::debug("hooks2", "Presented swapchain {:x} on queue {:x} successfully", - reinterpret_cast(*pPresentInfo->pSwapchains), - reinterpret_cast(queue)); return res; } + /// Erase the swapchain context and mapping when the swapchain is destroyed. void myvkDestroySwapchainKHR( VkDevice device, VkSwapchainKHR swapchain, - const VkAllocationCallbacks* pAllocator) { - swapchains.erase(swapchain); // erase swapchain context + const VkAllocationCallbacks* pAllocator) noexcept { + swapchains.erase(swapchain); swapchainToDeviceTable.erase(swapchain); - - Log::info("hooks", "Swapchain & Swapchain context destroyed successfully: {:x}", - reinterpret_cast(swapchain)); Layer::ovkDestroySwapchainKHR(device, swapchain, pAllocator); } } @@ -304,7 +270,6 @@ namespace { std::unordered_map Hooks::hooks = { // instance hooks {"vkCreateInstance", reinterpret_cast(myvkCreateInstance)}, - {"vkDestroyInstance", reinterpret_cast(myvkDestroyInstance)}, // device hooks {"vkCreateDevicePre", reinterpret_cast(myvkCreateDevicePre)}, diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index 8340464..4d10e08 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -5,11 +5,14 @@ #include -#include -#include +#include #include #include +#include +#include +#include #include +#include #include using namespace Utils; @@ -183,3 +186,23 @@ void Utils::copyImage(VkCommandBuffer buf, 1, &presentBarrier); } } + +namespace { + auto& logCounts() { + static std::unordered_map map; + return map; + } +} + +void Utils::logLimitN(const std::string& id, size_t n, const std::string& message) { + auto& count = logCounts()[id]; + if (count <= n) + std::cerr << "lsfg-vk: " << message << '\n'; + if (count == n) + std::cerr << "(above message has been repeated " << n << " times, suppressing further)\n"; + count++; +} + +void Utils::resetLimitN(const std::string& id) noexcept { + logCounts().erase(id); +}