From 6f516c3022830452d85abe39d7e163d47e6e652e Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Tue, 10 Sep 2024 23:47:15 +0100 Subject: [PATCH] layer: Send VK_ERROR_OUT_OF_DATE when window size changes on X11 As currentExtent will have changed... --- layer/VkLayer_FROG_gamescope_wsi.cpp | 24 +++++++- layer/vulkan_operators.hpp | 90 ++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 layer/vulkan_operators.hpp diff --git a/layer/VkLayer_FROG_gamescope_wsi.cpp b/layer/VkLayer_FROG_gamescope_wsi.cpp index 2baea4f0ca..8da45deafe 100644 --- a/layer/VkLayer_FROG_gamescope_wsi.cpp +++ b/layer/VkLayer_FROG_gamescope_wsi.cpp @@ -3,6 +3,7 @@ #define VK_USE_PLATFORM_XLIB_KHR #include "vkroots.h" #include "xcb_helpers.hpp" +#include "vulkan_operators.hpp" #include "gamescope-swapchain-client-protocol.h" #include "../src/color_helpers.h" #include "../src/layer_defines.h" @@ -209,7 +210,11 @@ namespace GamescopeWSILayer { GamescopeLayerClient::Flags flags; bool hdrOutput; + // Cached for comparison. + std::optional cachedWindowRect; + bool isWayland() const { + // Is native Wayland? return connection == nullptr; } @@ -222,7 +227,7 @@ namespace GamescopeWSILayer { return hdrOutput && hdrAllowed; } - bool canBypassXWayland() const { + bool canBypassXWayland() { if (isWayland()) return true; @@ -234,6 +239,8 @@ namespace GamescopeWSILayer { return false; } + cachedWindowRect = *rect; + auto toplevelRect = xcb::getWindowRect(connection, *toplevelWindow); if (!toplevelRect) { fprintf(stderr, "[Gamescope WSI] canBypassXWayland: failed to get window info for window 0x%x.\n", window); @@ -294,6 +301,7 @@ namespace GamescopeWSILayer { bool isBypassingXWayland; bool forceFifo; VkPresentModeKHR presentMode; + VkExtent2D extent; uint32_t serverId = 0; bool retired = false; @@ -1046,6 +1054,7 @@ namespace GamescopeWSILayer { .isBypassingXWayland = canBypass, .forceFifo = gamescopeIsForcingFifo(), // Were we forcing fifo when this swapchain was made? .presentMode = pCreateInfo->presentMode, // The new present mode. + .extent = pCreateInfo->imageExtent, .serverId = serverId, }); gamescopeSwapchain->pastPresentTimings.reserve(MaxPastPresentationTimes); @@ -1238,6 +1247,19 @@ namespace GamescopeWSILayer { const bool canBypass = gamescopeSurface->canBypassXWayland(); if (canBypass != gamescopeSwapchain->isBypassingXWayland) UpdateSwapchainResult(canBypass ? VK_SUBOPTIMAL_KHR : VK_ERROR_OUT_OF_DATE_KHR); + + // Emulate behaviour when currentExtent changes in X11 swapchain. + if (!gamescopeSurface->isWayland()) { + // gamescopeSurface->cachedWindowSize is set by canBypassXWayland. + // TODO: Rename that to be some update cached vars thing, then read back canBypassXWayland. + if (gamescopeSurface->cachedWindowRect) { + const bool windowSizeChanged = gamescopeSurface->cachedWindowRect->extent != gamescopeSwapchain->extent; + if (windowSizeChanged) + UpdateSwapchainResult(VK_ERROR_OUT_OF_DATE_KHR); + } else { + fprintf(stderr, "[Gamescope WSI] QueuePresentKHR: Failed to get cached window size for swapchain %u\n", i); + } + } } } diff --git a/layer/vulkan_operators.hpp b/layer/vulkan_operators.hpp new file mode 100644 index 0000000000..ab313dd6c8 --- /dev/null +++ b/layer/vulkan_operators.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include + +inline bool operator == ( + const VkImageSubresourceRange& a, + const VkImageSubresourceRange& b) { + return a.aspectMask == b.aspectMask + && a.baseMipLevel == b.baseMipLevel + && a.levelCount == b.levelCount + && a.baseArrayLayer == b.baseArrayLayer + && a.layerCount == b.layerCount; +} + + +inline bool operator != ( + const VkImageSubresourceRange& a, + const VkImageSubresourceRange& b) { + return !operator == (a, b); +} + + +inline bool operator == ( + const VkImageSubresourceLayers& a, + const VkImageSubresourceLayers& b) { + return a.aspectMask == b.aspectMask + && a.mipLevel == b.mipLevel + && a.baseArrayLayer == b.baseArrayLayer + && a.layerCount == b.layerCount; +} + + +inline bool operator != ( + const VkImageSubresourceLayers& a, + const VkImageSubresourceLayers& b) { + return !operator == (a, b); +} + + +inline bool operator == (VkExtent3D a, VkExtent3D b) { + return a.width == b.width + && a.height == b.height + && a.depth == b.depth; +} + + +inline bool operator != (VkExtent3D a, VkExtent3D b) { + return a.width != b.width + || a.height != b.height + || a.depth != b.depth; +} + + +inline bool operator == (VkExtent2D a, VkExtent2D b) { + return a.width == b.width + && a.height == b.height; +} + + +inline bool operator != (VkExtent2D a, VkExtent2D b) { + return a.width != b.width + || a.height != b.height; +} + + +inline bool operator == (VkOffset3D a, VkOffset3D b) { + return a.x == b.x + && a.y == b.y + && a.z == b.z; +} + + +inline bool operator != (VkOffset3D a, VkOffset3D b) { + return a.x != b.x + || a.y != b.y + || a.z != b.z; +} + + +inline bool operator == (VkOffset2D a, VkOffset2D b) { + return a.x == b.x + && a.y == b.y; +} + + +inline bool operator != (VkOffset2D a, VkOffset2D b) { + return a.x != b.x + || a.y != b.y; +} +