From bef170f9a49a7063247d17d7bbd194c3a311a481 Mon Sep 17 00:00:00 2001 From: sharkautarch <128002472+sharkautarch@users.noreply.github.com> Date: Tue, 30 Jan 2024 11:07:03 -0500 Subject: [PATCH 1/2] allow gamescope to run on devices that only support vulkan 1.2 --- src/rendervulkan.cpp | 50 +++++++++++++++++++++++++++++++++++++------- src/rendervulkan.hpp | 10 +++++++-- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp index 3570186070..7f689f6af1 100644 --- a/src/rendervulkan.cpp +++ b/src/rendervulkan.cpp @@ -277,8 +277,8 @@ bool CVulkanDevice::BInit(VkInstance instance, VkSurfaceKHR surface) std::thread piplelineThread([this](){compileAllPipelines();}); piplelineThread.detach(); - - g_reshadeManager.init(this); + if (m_supportsReshade) + g_reshadeManager.init(this); return true; } @@ -459,6 +459,7 @@ bool CVulkanDevice::createDevice() m_bSupportsModifiers = false; } + { VkPhysicalDeviceVulkan12Features vulkan12Features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, @@ -468,7 +469,6 @@ bool CVulkanDevice::createDevice() .pNext = &vulkan12Features, }; vk.GetPhysicalDeviceFeatures2( physDev(), &features2 ); - m_bSupportsFp16 = vulkan12Features.shaderFloat16 && features2.features.shaderInt16; } @@ -550,10 +550,20 @@ bool CVulkanDevice::createDevice() VkPhysicalDevicePresentWaitFeaturesKHR presentWaitFeatures = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR, - .pNext = &features13, .presentWait = VK_TRUE, }; - + + + VkPhysicalDeviceProperties props; + vkGetPhysicalDeviceProperties(physDev(), &props); + m_vkApiVer = props.apiVersion; + if (VK_API_VERSION_MINOR(m_vkApiVer) >= 3) + presentWaitFeatures.pNext = &features13; + else if (VK_API_VERSION_MINOR(m_vkApiVer) < 2) { + vk_log.errorf("ERROR: gamescope requires device support for at least vulkan 1.2"); + return false; + } + VkPhysicalDevicePresentIdFeaturesKHR presentIdFeatures = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR, .pNext = &presentWaitFeatures, @@ -623,6 +633,16 @@ bool CVulkanDevice::createDevice() #define VK_FUNC(x) vk.x = (PFN_vk##x) vk.GetDeviceProcAddr(device(), "vk"#x); VULKAN_DEVICE_FUNCTIONS #undef VK_FUNC + + if (VK_API_VERSION_MINOR(m_vkApiVer) >= 3) + { + #define VK_FUNC(x) vk.x = (PFN_vk##x) vk.GetDeviceProcAddr(device(), "vk"#x); + VULKAN_1_3_DEVICE_FUNCTIONS + #undef VK_FUNC + m_supportsReshade = true; + } else { + m_supportsReshade = false; //reshade effect manager uses vulkan 1.3 CmdBeginRendering/CmdEndRendering + } vk.GetDeviceQueue(device(), m_queueFamily, 0, &m_queue); if ( m_queueFamily == m_generalQueueFamily ) @@ -3286,13 +3306,27 @@ VkInstance vulkan_create_instance( void ) SDL_Vulkan_GetInstanceExtensions( nullptr, &extCount, sdlExtensions.data() ); } + + uint32_t pApiVersion = 0; + if (vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceVersion") == NULL) { + vk_log.errorf("ERROR: gamescope requires vulkan >= 1.2, instance only supports vulkan 1.0"); + exit(1); + } + + vkEnumerateInstanceVersion(&pApiVersion); + + if (VK_API_VERSION_MINOR(pApiVersion) < 2) { + vk_log.errorf("ERROR: gamescope requires device support for at least vulkan 1.2"); + exit(1); + } + const VkApplicationInfo appInfo = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pApplicationName = "gamescope", .applicationVersion = VK_MAKE_VERSION(1, 0, 0), .pEngineName = "hopefully not just some code", .engineVersion = VK_MAKE_VERSION(1, 0, 0), - .apiVersion = VK_API_VERSION_1_3, + .apiVersion = pApiVersion, }; const VkInstanceCreateInfo createInfo = { @@ -3687,7 +3721,7 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, std::sh if (!frameInfo->applyOutputColorMgmt) outputTF = EOTF_Count; //Disable blending stuff. - if (!g_reshade_effect.empty()) + if (g_device.m_supportsReshade && !g_reshade_effect.empty()) { if (frameInfo->layers[0].tex) { @@ -3709,7 +3743,7 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, std::sh } } } - else + else if (g_device.m_supportsReshade) { g_reshadeManager.clear(); } diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp index 992438d53c..7bc51b6897 100644 --- a/src/rendervulkan.hpp +++ b/src/rendervulkan.hpp @@ -634,7 +634,6 @@ static inline uint32_t div_roundup(uint32_t x, uint32_t y) VK_FUNC(BeginCommandBuffer) \ VK_FUNC(BindBufferMemory) \ VK_FUNC(BindImageMemory) \ - VK_FUNC(CmdBeginRendering) \ VK_FUNC(CmdBindDescriptorSets) \ VK_FUNC(CmdBindPipeline) \ VK_FUNC(CmdClearColorImage) \ @@ -642,7 +641,6 @@ static inline uint32_t div_roundup(uint32_t x, uint32_t y) VK_FUNC(CmdCopyImage) \ VK_FUNC(CmdDispatch) \ VK_FUNC(CmdDraw) \ - VK_FUNC(CmdEndRendering) \ VK_FUNC(CmdPipelineBarrier) \ VK_FUNC(CmdPushConstants) \ VK_FUNC(CreateBuffer) \ @@ -694,6 +692,10 @@ static inline uint32_t div_roundup(uint32_t x, uint32_t y) VK_FUNC(WaitSemaphores) \ VK_FUNC(SetHdrMetadataEXT) +#define VULKAN_1_3_DEVICE_FUNCTIONS \ + VK_FUNC(CmdBeginRendering) \ + VK_FUNC(CmdEndRendering) + template constexpr T align(T what, U to) { return (what + to - 1) & ~(to - 1); @@ -702,6 +704,7 @@ return (what + to - 1) & ~(to - 1); class CVulkanDevice { public: + bool m_supportsReshade = false; bool BInit(VkInstance instance, VkSurfaceKHR surface); VkSampler sampler(SamplerState key); @@ -760,6 +763,7 @@ class CVulkanDevice { VULKAN_INSTANCE_FUNCTIONS VULKAN_DEVICE_FUNCTIONS + VULKAN_1_3_DEVICE_FUNCTIONS } vk; #undef VK_FUNC @@ -824,6 +828,8 @@ class CVulkanDevice std::atomic m_submissionSeqNo = { 0 }; std::vector> m_unusedCmdBufs; std::map> m_pendingCmdBufs; + + uint32_t m_vkApiVer = 0; }; struct TextureState From e072e39d4938cc923642a462ae7338352e3f3325 Mon Sep 17 00:00:00 2001 From: sharkautarch <128002472+sharkautarch@users.noreply.github.com> Date: Sat, 3 Feb 2024 14:04:20 -0500 Subject: [PATCH 2/2] Allow gamescope to work on devices that don't support VK_KHR_present_id and/or VK_KHR_present_wait --- src/main.cpp | 35 ++++++++--- src/rendervulkan.cpp | 141 ++++++++++++++++++++++++++++++++++--------- src/rendervulkan.hpp | 21 +++++-- 3 files changed, 155 insertions(+), 42 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 02cc5ce3a6..4e50b2a3bb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -329,7 +329,7 @@ bool BIsSDLSession( void ) } -static bool initOutput(int preferredWidth, int preferredHeight, int preferredRefresh); +static bool initOutput(int preferredWidth, int preferredHeight, int preferredRefresh, bool bSupportsPresentWait); static void steamCompMgrThreadRun(int argc, char **argv); static std::string build_optstring(const struct option *options) @@ -735,10 +735,13 @@ int main(int argc, char **argv) g_pOriginalDisplay = getenv("DISPLAY"); g_pOriginalWaylandDisplay = getenv("WAYLAND_DISPLAY"); g_bIsNested = g_pOriginalDisplay != NULL || g_pOriginalWaylandDisplay != NULL; - + + const bool bSupportsPresentWait = checkForPresentWaitExt(); + if ( BIsSDLSession() && g_pOriginalWaylandDisplay != NULL ) { - if (CheckWaylandPresentationTime()) + const bool bCompositor_supports_present_wait = CheckWaylandPresentationTime(); + if (bCompositor_supports_present_wait && bSupportsPresentWait) { // Default to SDL_VIDEODRIVER wayland under Wayland and force enable vk_khr_present_wait // (not enabled by default in Mesa because instance does not know if Wayland @@ -746,14 +749,30 @@ int main(int argc, char **argv) setenv("vk_khr_present_wait", "true", 0); setenv("SDL_VIDEODRIVER", "wayland", 0); } - else + else if (bSupportsPresentWait) { fprintf(stderr, "Your Wayland compositor does NOT support wp_presentation/presentation-time which is required for VK_KHR_present_wait and VK_KHR_present_id.\n" "Please complain to your compositor vendor for support. Falling back to X11 window with less accurate present wait.\n"); setenv("SDL_VIDEODRIVER", "x11", 1); } + else if (!bSupportsPresentWait) + { + fprintf(stderr, + "Notice: GPU device does not support present_wait extension\n" + "continuing with present_wait disabled.\n\n"); + setenv("vk_khr_present_wait", "false", 1); + if (bCompositor_supports_present_wait) + setenv("SDL_VIDEODRIVER", "wayland", 0); //doing this for consistency, shouldn't cause any issues + } } + else if (!bSupportsPresentWait) + { + fprintf(stderr, + "Notice: GPU device does not support present_wait extension\n" + "continuing with present_wait disabled.\n\n"); + setenv("vk_khr_present_wait", "false", 1); + } if ( !wlsession_init() ) { @@ -786,7 +805,7 @@ int main(int argc, char **argv) } #endif - if ( !initOutput( g_nPreferredOutputWidth, g_nPreferredOutputHeight, g_nNestedRefresh ) ) + if ( !initOutput( g_nPreferredOutputWidth, g_nPreferredOutputHeight, g_nNestedRefresh, bSupportsPresentWait ) ) { fprintf( stderr, "Failed to initialize output\n" ); return 1; @@ -910,7 +929,7 @@ static void steamCompMgrThreadRun(int argc, char **argv) pthread_kill( g_mainThread, SIGINT ); } -static bool initOutput( int preferredWidth, int preferredHeight, int preferredRefresh ) +static bool initOutput( int preferredWidth, int preferredHeight, int preferredRefresh, bool bSupportsPresentWait ) { VkInstance instance = vulkan_create_instance(); @@ -960,7 +979,7 @@ static bool initOutput( int preferredWidth, int preferredHeight, int preferredRe } } - if ( !vulkan_init( instance, surface ) ) + if ( !vulkan_init( instance, surface, bSupportsPresentWait ) ) { fprintf( stderr, "Failed to initialize Vulkan\n" ); return false; @@ -970,7 +989,7 @@ static bool initOutput( int preferredWidth, int preferredHeight, int preferredRe } else { - if ( !vulkan_init( instance, VK_NULL_HANDLE ) ) + if ( !vulkan_init( instance, VK_NULL_HANDLE, bSupportsPresentWait ) ) { fprintf( stderr, "Failed to initialize Vulkan\n" ); return false; diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp index 7f689f6af1..6fee0da50c 100644 --- a/src/rendervulkan.cpp +++ b/src/rendervulkan.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #if defined(__linux__) #include @@ -248,7 +249,7 @@ uint32_t DRMFormatGetBPP( uint32_t nDRMFormat ) return false; } -bool CVulkanDevice::BInit(VkInstance instance, VkSurfaceKHR surface) +bool CVulkanDevice::BInit(VkInstance instance, VkSurfaceKHR surface, bool bSupportsPresentWait) { assert(instance); assert(!m_bInitialized); @@ -262,7 +263,7 @@ bool CVulkanDevice::BInit(VkInstance instance, VkSurfaceKHR surface) if (!selectPhysDev(surface)) return false; - if (!createDevice()) + if (!createDevice(bSupportsPresentWait)) return false; if (!createLayouts()) return false; @@ -375,8 +376,66 @@ bool CVulkanDevice::selectPhysDev(VkSurfaceKHR surface) return true; } -bool CVulkanDevice::createDevice() +void CVulkanDevice::prepare_tmp_dev(VkInstance tmp_instance) { + //g_output.surface = VK_NULL_HANDLE; + #define VK_FUNC(x) vk.x = (PFN_vk##x) vkGetInstanceProcAddr(tmp_instance, "vk"#x); + VULKAN_INSTANCE_FUNCTIONS + #undef VK_FUNC + + + m_instance = tmp_instance; + + selectPhysDev(VK_NULL_HANDLE); +} + +bool CVulkanDevice::_checkForPresentWaitExt() +{ + + vk.GetPhysicalDeviceMemoryProperties( physDev(), &m_memoryProperties ); + + uint32_t supportedExtensionCount; + vk.EnumerateDeviceExtensionProperties( physDev(), NULL, &supportedExtensionCount, NULL ); + + std::vector supportedExts(supportedExtensionCount); + vk.EnumerateDeviceExtensionProperties( physDev(), NULL, &supportedExtensionCount, supportedExts.data() ); + + bool bSupports_present_wait, bSupports_present_id = false; + + for ( uint32_t i = 0; i < supportedExtensionCount; ++i ) + { + if ( strcmp(supportedExts[i].extensionName, VK_KHR_PRESENT_ID_EXTENSION_NAME) == 0 ) + bSupports_present_id = true; + else if ( strcmp(supportedExts[i].extensionName, VK_KHR_PRESENT_WAIT_EXTENSION_NAME) == 0) + bSupports_present_wait = true; + } + + return bSupports_present_wait && bSupports_present_id; +} + +bool checkForPresentWaitExt() +{ + const char * const prevLayerDisable_env = getenv("VK_LOADER_LAYERS_DISABLE"); + setenv("VK_LOADER_LAYERS_DISABLE", "~all~", 1); + void * temp_vulkan_lib = dlopen("libvulkan.so.1", RTLD_LAZY | RTLD_LOCAL); + VkInstance tmp_instance = vulkan_create_instance(true); + CVulkanDevice tmp_dev; + tmp_dev.prepare_tmp_dev(tmp_instance); + + const bool bSupportsPresentWait = tmp_dev._checkForPresentWaitExt(); + + dlclose(temp_vulkan_lib); + if ( prevLayerDisable_env != nullptr ) + setenv("VK_LOADER_LAYERS_DISABLE", prevLayerDisable_env, 1); + else + unsetenv("VK_LOADER_LAYERS_DISABLE"); + + return bSupportsPresentWait; +} + +bool CVulkanDevice::createDevice(bool bPresentWaitSupported) +{ + m_supports_present_wait = bPresentWaitSupported; vk.GetPhysicalDeviceMemoryProperties( physDev(), &m_memoryProperties ); uint32_t supportedExtensionCount; @@ -504,9 +563,10 @@ bool CVulkanDevice::createDevice() { enabledExtensions.push_back( VK_KHR_SWAPCHAIN_EXTENSION_NAME ); enabledExtensions.push_back( VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME ); - - enabledExtensions.push_back( VK_KHR_PRESENT_ID_EXTENSION_NAME ); - enabledExtensions.push_back( VK_KHR_PRESENT_WAIT_EXTENSION_NAME ); + if (m_supports_present_wait) { + enabledExtensions.push_back( VK_KHR_PRESENT_ID_EXTENSION_NAME ); + enabledExtensions.push_back( VK_KHR_PRESENT_WAIT_EXTENSION_NAME ); + } } if ( m_bSupportsModifiers ) @@ -572,11 +632,15 @@ bool CVulkanDevice::createDevice() VkPhysicalDeviceFeatures2 features2 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, - .pNext = &presentIdFeatures, .features = { .shaderInt16 = m_bSupportsFp16, }, }; + + if (m_supports_present_wait) + { + features2.pNext = &presentIdFeatures; + } VkDeviceCreateInfo deviceCreateInfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, @@ -2775,16 +2839,21 @@ void vulkan_present_to_window( void ) VkPresentInfoKHR presentInfo = { .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, - .pNext = &presentIdInfo, .swapchainCount = 1, .pSwapchains = &g_output.swapChain, .pImageIndices = &g_output.nOutImage, }; + + if ( g_device.m_supports_present_wait) + { + presentInfo.pNext = &presentIdInfo; + } if ( g_device.vk.QueuePresentKHR( g_device.queue(), &presentInfo ) == VK_SUCCESS ) { g_currentPresentWaitId = presentId; - g_currentPresentWaitId.notify_all(); + if ( g_device.m_supports_present_wait ) + g_currentPresentWaitId.notify_all(); } else vulkan_remake_swapchain(); @@ -3032,26 +3101,40 @@ bool vulkan_make_swapchain( VulkanOutput_t *pOutput ) return true; } + bool vulkan_remake_swapchain( void ) { - std::unique_lock lock(present_wait_lock); - g_currentPresentWaitId = 0; - g_currentPresentWaitId.notify_all(); + bool bRet; + + auto doit = [&](std::function f) { + VulkanOutput_t *pOutput = &g_output; + g_device.waitIdle(); + f(); - VulkanOutput_t *pOutput = &g_output; - g_device.waitIdle(); - g_device.vk.QueueWaitIdle( g_device.queue() ); + pOutput->outputImages.clear(); - pOutput->outputImages.clear(); + g_device.vk.DestroySwapchainKHR( g_device.device(), pOutput->swapChain, nullptr ); - g_device.vk.DestroySwapchainKHR( g_device.device(), pOutput->swapChain, nullptr ); + // Delete screenshot image to be remade if needed + for (auto& pScreenshotImage : pOutput->pScreenshotImages) + pScreenshotImage = nullptr; - // Delete screenshot image to be remade if needed - for (auto& pScreenshotImage : pOutput->pScreenshotImages) - pScreenshotImage = nullptr; - - bool bRet = vulkan_make_swapchain( pOutput ); - assert( bRet ); // Something has gone horribly wrong! + bRet = vulkan_make_swapchain( pOutput ); + assert( bRet ); // Something has gone horribly wrong! + }; + + if (g_device.m_supports_present_wait) + { + std::unique_lock lock(present_wait_lock); + g_currentPresentWaitId = 0; + g_currentPresentWaitId.notify_all(); + doit( [&](){g_device.vk.QueueWaitIdle( g_device.queue() );}); + } + else + { + doit([&](){return;}); + } + return bRet; } @@ -3281,18 +3364,18 @@ static bool init_nis_data() return true; } -VkInstance vulkan_create_instance( void ) +VkInstance vulkan_create_instance( bool is_temp_instance ) { VkResult result = VK_ERROR_INITIALIZATION_FAILED; std::vector< const char * > sdlExtensions; - if ( BIsVRSession() ) + if ( !is_temp_instance && BIsVRSession() ) { #if HAVE_OPENVR vrsession_append_instance_exts( sdlExtensions ); #endif } - else if ( BIsSDLSession() ) + else if ( !is_temp_instance && BIsSDLSession() ) { if ( SDL_Vulkan_LoadLibrary( nullptr ) != 0 ) { @@ -3346,15 +3429,15 @@ VkInstance vulkan_create_instance( void ) return instance; } -bool vulkan_init( VkInstance instance, VkSurfaceKHR surface ) +bool vulkan_init( VkInstance instance, VkSurfaceKHR surface, bool bSupportsPresentWait ) { - if (!g_device.BInit(instance, surface)) + if (!g_device.BInit(instance, surface, bSupportsPresentWait)) return false; if (!init_nis_data()) return false; - if (BIsNested() && !BIsVRSession()) + if (bSupportsPresentWait && BIsNested() && !BIsVRSession()) { std::thread present_wait_thread( present_wait_thread_func ); present_wait_thread.detach(); diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp index af9d6e27d4..d22b9e54fe 100644 --- a/src/rendervulkan.hpp +++ b/src/rendervulkan.hpp @@ -367,8 +367,8 @@ namespace CompositeDebugFlag static constexpr uint32_t Tonemap_Reinhard = 1u << 7; }; -VkInstance vulkan_create_instance(void); -bool vulkan_init(VkInstance instance, VkSurfaceKHR surface); +VkInstance vulkan_create_instance(bool is_temp_instance = false); +bool vulkan_init(VkInstance instance, VkSurfaceKHR surface, bool bSupportsPresentWait); bool vulkan_init_formats(void); bool vulkan_make_output(); @@ -700,6 +700,9 @@ static inline uint32_t div_roundup(uint32_t x, uint32_t y) VK_FUNC(CmdBeginRendering) \ VK_FUNC(CmdEndRendering) + +bool checkForPresentWaitExt(); + template constexpr T align(T what, U to) { return (what + to - 1) & ~(to - 1); @@ -708,11 +711,15 @@ return (what + to - 1) & ~(to - 1); class CVulkanDevice { public: + bool m_supports_present_wait = true; bool m_supportsReshade = false; - bool BInit(VkInstance instance, VkSurfaceKHR surface); + bool BInit(VkInstance instance, VkSurfaceKHR surface, bool bSupportsPresentWait); VkSampler sampler(SamplerState key); VkPipeline pipeline(ShaderType type, uint32_t layerCount = 1, uint32_t ycbcrMask = 0, uint32_t blur_layers = 0, uint32_t colorspace_mask = 0, uint32_t output_eotf = EOTF_Gamma22, bool itm_enable = false); + + bool checkForPresentWaitExt(); + int32_t findMemoryType( VkMemoryPropertyFlags properties, uint32_t requiredTypeBits ); std::unique_ptr commandBuffer(); uint64_t submit( std::unique_ptr cmdBuf); @@ -775,9 +782,13 @@ class CVulkanDevice protected: friend class CVulkanCmdBuffer; - + + void prepare_tmp_dev(VkInstance tmp_instance); + bool _checkForPresentWaitExt(); + friend bool checkForPresentWaitExt(); + bool selectPhysDev(VkSurfaceKHR surface); - bool createDevice(); + bool createDevice(bool bSupportsPresentWait); bool createLayouts(); bool createPools(); bool createShaders();