From 4aa431b174c37b3bc0748ddc9bd2ca9c808881e2 Mon Sep 17 00:00:00 2001
From: Bas Nieuwenhuizen <bas@basnieuwenhuizen.nl>
Date: Fri, 14 Jan 2022 15:58:45 +0100
Subject: [PATCH 5/8] STEAMOS: radv: min image count override for FH5

Otherwise in combination with the vblank time reservation in
gamescope the game could get stuck in low power states.
---
 src/util/00-radv-defaults.conf | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/util/00-radv-defaults.conf b/src/util/00-radv-defaults.conf
index 9434dba1e3f..3c3ae562eb9 100644
--- a/src/util/00-radv-defaults.conf
+++ b/src/util/00-radv-defaults.conf
@@ -186,5 +186,9 @@ Application bugs worked around in this file:
         <application name="Rocket League" executable="RocketLeague">
             <option name="radv_zero_vram" value="true" />
         </application>
+
+        <application name="Forza Horizon 5" application_name_match="ForzaHorizon5.exe">
+            <option name="vk_x11_override_min_image_count" value="4" />
+        </application>
     </device>
 </driconf>
-- 
2.42.0


From 88327ee8507a323f48e43eeb87cf1f102d04f174 Mon Sep 17 00:00:00 2001
From: Bas Nieuwenhuizen <bas@basnieuwenhuizen.nl>
Date: Mon, 21 Feb 2022 18:43:54 +0100
Subject: [PATCH 6/8] STEAMOS: Dynamic swapchain override for gamescope limiter

---
 src/loader/loader_dri3_helper.c | 42 +++++++++++++++++++++++++++++++--
 src/loader/loader_dri3_helper.h |  1 +
 src/loader/meson.build          |  2 +-
 src/vulkan/wsi/wsi_common_x11.c | 38 +++++++++++++++++++++++++++++
 4 files changed, 80 insertions(+), 3 deletions(-)

diff --git a/src/loader/loader_dri3_helper.c b/src/loader/loader_dri3_helper.c
index 32135770e9d..abc79dda97e 100644
--- a/src/loader/loader_dri3_helper.c
+++ b/src/loader/loader_dri3_helper.c
@@ -289,6 +289,30 @@ dri3_update_max_num_back(struct loader_dri3_drawable *draw)
    }
 }
 
+static unsigned
+gamescope_swapchain_override()
+{
+   const char *path = getenv("GAMESCOPE_LIMITER_FILE");
+   if (!path)
+      return 0;
+
+   static simple_mtx_t mtx = SIMPLE_MTX_INITIALIZER;
+   static int fd = -1;
+
+   simple_mtx_lock(&mtx);
+   if (fd < 0) {
+      fd = open(path, O_RDONLY);
+   }
+   simple_mtx_unlock(&mtx);
+
+   if (fd < 0)
+      return 0;
+
+   uint32_t override_value = 0;
+   pread(fd, &override_value, sizeof(override_value), 0);
+   return override_value;
+}
+
 void
 loader_dri3_set_swap_interval(struct loader_dri3_drawable *draw, int interval)
 {
@@ -303,10 +327,12 @@ loader_dri3_set_swap_interval(struct loader_dri3_drawable *draw, int interval)
     * PS. changing from value A to B and A < B won't cause swap out of order but
     * may still gets wrong target_msc value at the beginning.
     */
-   if (draw->swap_interval != interval)
+   if (draw->orig_swap_interval != interval)
       loader_dri3_swapbuffer_barrier(draw);
 
-   draw->swap_interval = interval;
+   draw->orig_swap_interval = interval;
+   if (gamescope_swapchain_override() != 1)
+      draw->swap_interval = interval;
 }
 
 static void
@@ -438,6 +464,12 @@ loader_dri3_drawable_init(xcb_connection_t *conn,
    draw->swap_interval = dri_get_initial_swap_interval(draw->dri_screen_render_gpu,
                                                        draw->ext->config);
 
+   draw->orig_swap_interval = draw->swap_interval;
+
+   unsigned gamescope_override = gamescope_swapchain_override();
+   if (gamescope_override == 1)
+      draw->swap_interval = 1;
+
    dri3_update_max_num_back(draw);
 
    /* Create a new drawable */
@@ -1092,6 +1124,12 @@ loader_dri3_swap_buffers_msc(struct loader_dri3_drawable *draw,
    if (draw->type == LOADER_DRI3_DRAWABLE_WINDOW) {
       dri3_fence_reset(draw->conn, back);
 
+      unsigned gamescope_override = gamescope_swapchain_override();
+      if (gamescope_override == 1)
+         draw->swap_interval = 1;
+      else
+         draw->swap_interval = draw->orig_swap_interval;
+
       /* Compute when we want the frame shown by taking the last known
        * successful MSC and adding in a swap interval for each outstanding swap
        * request. target_msc=divisor=remainder=0 means "Use glXSwapBuffers()
diff --git a/src/loader/loader_dri3_helper.h b/src/loader/loader_dri3_helper.h
index 1fd340bd145..b8f5eaaf190 100644
--- a/src/loader/loader_dri3_helper.h
+++ b/src/loader/loader_dri3_helper.h
@@ -178,6 +178,7 @@ struct loader_dri3_drawable {
    bool block_on_depleted_buffers;
    bool queries_buffer_age;
    int swap_interval;
+   int orig_swap_interval;
 
    struct loader_dri3_extensions *ext;
    const struct loader_dri3_vtable *vtable;
diff --git a/src/loader/meson.build b/src/loader/meson.build
index 35f9991ba2f..154cf809a69 100644
--- a/src/loader/meson.build
+++ b/src/loader/meson.build
@@ -29,7 +29,7 @@ if with_platform_x11 and with_dri3
     dependencies : [
       idep_mesautil,
       dep_libdrm, dep_xcb_dri3, dep_xcb_present, dep_xcb_sync, dep_xshmfence,
-      dep_xcb_xfixes,
+      dep_xcb_xfixes, dep_xcb_xrandr, idep_mesautil
     ],
     build_by_default : false,
   )
diff --git a/src/vulkan/wsi/wsi_common_x11.c b/src/vulkan/wsi/wsi_common_x11.c
index 72ff193d30a..996fc230673 100644
--- a/src/vulkan/wsi/wsi_common_x11.c
+++ b/src/vulkan/wsi/wsi_common_x11.c
@@ -48,6 +48,7 @@
 #include "util/hash_table.h"
 #include "util/os_file.h"
 #include "util/os_time.h"
+#include "util/simple_mtx.h"
 #include "util/u_debug.h"
 #include "util/u_thread.h"
 #include "util/xmlconfig.h"
@@ -219,6 +220,30 @@ wsi_x11_detect_xwayland(xcb_connection_t *conn,
    return is_xwayland;
 }
 
+static unsigned
+gamescope_swapchain_override()
+{
+   const char *path = getenv("GAMESCOPE_LIMITER_FILE");
+   if (!path)
+      return 0;
+
+   static simple_mtx_t mtx = SIMPLE_MTX_INITIALIZER;
+   static int fd = -1;
+
+   simple_mtx_lock(&mtx);
+   if (fd < 0) {
+      fd = open(path, O_RDONLY);
+   }
+   simple_mtx_unlock(&mtx);
+
+   if (fd < 0)
+      return 0;
+
+   uint32_t override_value = 0;
+   pread(fd, &override_value, sizeof(override_value), 0);
+   return override_value;
+}
+
 static struct wsi_x11_connection *
 wsi_x11_connection_create(struct wsi_device *wsi_dev,
                           xcb_connection_t *conn)
@@ -1103,6 +1128,8 @@ struct x11_swapchain {
    /* Total number of images returned to application in AcquireNextImage. */
    uint64_t                                     present_poll_acquire_count;
 
+   VkPresentModeKHR                             orig_present_mode;
+
    struct x11_image                             images[0];
 };
 VK_DEFINE_NONDISP_HANDLE_CASTS(x11_swapchain, base.base, VkSwapchainKHR,
@@ -1852,6 +1879,12 @@ x11_queue_present(struct wsi_swapchain *anv_chain,
    if (chain->status < 0)
       return chain->status;
 
+   unsigned gamescope_override = gamescope_swapchain_override();
+   if ((gamescope_override == 1 && chain->base.present_mode != VK_PRESENT_MODE_FIFO_KHR) ||
+       (gamescope_override != 1 && chain->base.present_mode != chain->orig_present_mode)) {
+      return x11_swapchain_result(chain, VK_ERROR_OUT_OF_DATE_KHR);
+   }
+
    if (damage && damage->pRectangles && damage->rectangleCount > 0 &&
       damage->rectangleCount <= MAX_DAMAGE_RECTS) {
       xcb_rectangle_t rects[MAX_DAMAGE_RECTS];
@@ -2610,6 +2643,10 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
    xcb_void_cookie_t cookie;
    VkResult result;
    VkPresentModeKHR present_mode = wsi_swapchain_get_present_mode(wsi_device, pCreateInfo);
+   VkPresentModeKHR orig_present_mode = present_mode;
+
+   if (gamescope_swapchain_override() == 1)
+      present_mode = VK_PRESENT_MODE_FIFO_KHR;
 
    assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
 
@@ -2722,6 +2759,7 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
    chain->base.wait_for_present = x11_wait_for_present;
    chain->base.release_images = x11_release_images;
    chain->base.present_mode = present_mode;
+   chain->orig_present_mode = orig_present_mode;
    chain->base.image_count = num_images;
    chain->conn = conn;
    chain->window = window;
-- 
2.42.0

