From 3f4da265c5de52f4b56e2e1a6dcf7ed5428f4b7c Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis <5252246+antheas@users.noreply.github.com> Date: Thu, 2 Jan 2025 18:18:38 +0200 Subject: [PATCH] chore(gamescope): update, fix oxp x1 intel, cleanup (#2047) --- spec_files/gamescope/1671.patch | 54 -- spec_files/gamescope/740.patch | 1012 --------------------------- spec_files/gamescope/gamescope.spec | 10 +- spec_files/gamescope/handheld.patch | 892 +++++++++++++++++++++-- 4 files changed, 837 insertions(+), 1131 deletions(-) delete mode 100644 spec_files/gamescope/1671.patch delete mode 100644 spec_files/gamescope/740.patch diff --git a/spec_files/gamescope/1671.patch b/spec_files/gamescope/1671.patch deleted file mode 100644 index 846c228d..00000000 --- a/spec_files/gamescope/1671.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 4da5c0a60ece2955cd60166ad0f93c93b0871723 Mon Sep 17 00:00:00 2001 -From: brainantifreeze -Date: Thu, 19 Dec 2024 09:16:15 +0000 -Subject: [PATCH] add layer env var to hide present wait ext - ---- - layer/VkLayer_FROG_gamescope_wsi.cpp | 20 +++++++++++++++++++- - 1 file changed, 19 insertions(+), 1 deletion(-) - -diff --git a/layer/VkLayer_FROG_gamescope_wsi.cpp b/layer/VkLayer_FROG_gamescope_wsi.cpp -index 718a2604f3..f33da7f22f 100644 ---- a/layer/VkLayer_FROG_gamescope_wsi.cpp -+++ b/layer/VkLayer_FROG_gamescope_wsi.cpp -@@ -496,7 +496,11 @@ namespace GamescopeWSILayer { - createInfo.ppEnabledExtensionNames = enabledExts.data(); - - setenv("vk_xwayland_wait_ready", "false", 0); -- setenv("vk_khr_present_wait", "true", 0); -+ if (getHidePresentWait()) { -+ setenv("vk_khr_present_wait", "false", 0); -+ } else { -+ setenv("vk_khr_present_wait", "true", 0); -+ } - - VkResult result = pfnCreateInstanceProc(&createInfo, pAllocator, pInstance); - if (result != VK_SUCCESS) -@@ -801,6 +805,10 @@ namespace GamescopeWSILayer { - const vkroots::VkInstanceDispatch* pDispatch, - VkPhysicalDevice physicalDevice, - VkPhysicalDeviceFeatures2* pFeatures) { -+ if (getHidePresentWait()) { -+ fprintf(stderr, "[Gamescope WSI] Removing VkPhysicalDevicePresentWaitFeaturesKHR because GAMESCOPE_WSI_HIDE_PRESENT_WAIT_EXT is set\n"); -+ vkroots::RemoveFromChain(pFeatures); -+ } - pDispatch->GetPhysicalDeviceFeatures2(physicalDevice, pFeatures); - } - -@@ -1015,6 +1023,16 @@ namespace GamescopeWSILayer { - return value; - } - -+ static bool getHidePresentWait() { -+ static bool s_hidePresentWait = []() -> bool { -+ if (auto hide = parseEnv("GAMESCOPE_WSI_HIDE_PRESENT_WAIT_EXT")) { -+ return *hide == 1; -+ } -+ return false; -+ }(); -+ return s_hidePresentWait; -+ } -+ - static uint32_t getMinImageCount() { - static uint32_t s_minImageCount = []() -> uint32_t { - if (auto minCount = parseEnv("GAMESCOPE_WSI_MIN_IMAGE_COUNT")) { diff --git a/spec_files/gamescope/740.patch b/spec_files/gamescope/740.patch deleted file mode 100644 index 97a763f3..00000000 --- a/spec_files/gamescope/740.patch +++ /dev/null @@ -1,1012 +0,0 @@ -From 564a689c2b293d2e894bf1edb14eb0f78bde8725 Mon Sep 17 00:00:00 2001 -From: "Ruan E. Formigoni" -Date: Sat, 5 Oct 2024 14:40:23 -0300 -Subject: [PATCH 1/9] Partial bicubic code adaptation - ---- - src/Backends/SDLBackend.cpp | 4 + - src/Backends/WaylandBackend.cpp | 1 + - src/main.cpp | 29 ++++++ - src/main.hpp | 15 +++ - src/meson.build | 1 + - src/rendervulkan.cpp | 64 +++++++++++- - src/rendervulkan.hpp | 2 + - src/shaders/bicubic.h | 44 ++++++++ - src/shaders/cs_bicubic.comp | 176 ++++++++++++++++++++++++++++++++ - src/steamcompmgr.cpp | 6 ++ - 10 files changed, 341 insertions(+), 1 deletion(-) - create mode 100644 src/shaders/bicubic.h - create mode 100644 src/shaders/cs_bicubic.comp - -diff --git a/src/Backends/SDLBackend.cpp b/src/Backends/SDLBackend.cpp -index 6d50f8d34..c24b864c6 100644 ---- a/src/Backends/SDLBackend.cpp -+++ b/src/Backends/SDLBackend.cpp -@@ -719,6 +719,10 @@ namespace gamescope - case KEY_B: - g_wantedUpscaleFilter = GamescopeUpscaleFilter::LINEAR; - break; -+ case KEY_K: -+ g_wantedDownscaleFilter = (g_wantedDownscaleFilter == GamescopeDownscaleFilter::BICUBIC) ? -+ GamescopeDownscaleFilter::LINEAR : GamescopeDownscaleFilter::BICUBIC; -+ break; - case KEY_U: - g_wantedUpscaleFilter = (g_wantedUpscaleFilter == GamescopeUpscaleFilter::FSR) ? - GamescopeUpscaleFilter::LINEAR : GamescopeUpscaleFilter::FSR; -diff --git a/src/Backends/WaylandBackend.cpp b/src/Backends/WaylandBackend.cpp -index c53bf14df..d0abb2fa2 100644 ---- a/src/Backends/WaylandBackend.cpp -+++ b/src/Backends/WaylandBackend.cpp -@@ -1598,6 +1598,7 @@ namespace gamescope - bNeedsFullComposite |= cv_composite_force; - bNeedsFullComposite |= pFrameInfo->useFSRLayer0; - bNeedsFullComposite |= pFrameInfo->useNISLayer0; -+ bNeedsFullComposite |= pFrameInfo->useBICUBICLayer0; - bNeedsFullComposite |= pFrameInfo->blurLayer0; - bNeedsFullComposite |= bNeedsCompositeFromFilter; - bNeedsFullComposite |= g_bColorSliderInUse; -diff --git a/src/main.cpp b/src/main.cpp -index a9cdaa2e2..ed7fd4fa7 100644 ---- a/src/main.cpp -+++ b/src/main.cpp -@@ -5,6 +5,7 @@ - #include - #include - #include -+#include - #include - #if defined(__linux__) - #include -@@ -63,6 +64,7 @@ const struct option *gamescope_options = (struct option[]){ - { "output-height", required_argument, nullptr, 'H' }, - { "sharpness", required_argument, nullptr, 0 }, - { "fsr-sharpness", required_argument, nullptr, 0 }, -+ { "bicubic", optional_argument, nullptr, 'D' }, - { "rt", no_argument, nullptr, 0 }, - { "prefer-vk-device", required_argument, 0 }, - { "expose-wayland", no_argument, 0 }, -@@ -288,11 +290,14 @@ bool g_bGrabbed = false; - float g_mouseSensitivity = 1.0; - - GamescopeUpscaleFilter g_upscaleFilter = GamescopeUpscaleFilter::LINEAR; -+GamescopeDownscaleFilter g_downscaleFilter = GamescopeDownscaleFilter::LINEAR; - GamescopeUpscaleScaler g_upscaleScaler = GamescopeUpscaleScaler::AUTO; - - GamescopeUpscaleFilter g_wantedUpscaleFilter = GamescopeUpscaleFilter::LINEAR; -+GamescopeDownscaleFilter g_wantedDownscaleFilter = GamescopeDownscaleFilter::LINEAR; - GamescopeUpscaleScaler g_wantedUpscaleScaler = GamescopeUpscaleScaler::AUTO; - int g_upscaleFilterSharpness = 2; -+GamescopeBicubicParams g_bicubicParams; - - gamescope::GamescopeModeGeneration g_eGamescopeModeGeneration = gamescope::GAMESCOPE_MODE_GENERATE_CVT; - -@@ -397,6 +402,27 @@ static enum GamescopeUpscaleFilter parse_upscaler_filter(const char *str) - } - } - -+static enum GamescopeDownscaleFilter parse_downscaling_filter(const char *str) -+{ -+ std::stringstream ss{optarg}; -+ -+ double b, c; -+ char comma; -+ if ((ss >> b >> comma >> c) && (comma == ',')) { -+ // clamp values -+ b = std::clamp(b, 0.0, 1.0); -+ c = std::clamp(c, 0.0, 1.0); -+ // Ovewrite default global parameters -+ g_bicubicParams.b = b; -+ g_bicubicParams.c = c; -+ // Set downscaling filters -+ return GamescopeDownscaleFilter::BICUBIC; -+ } -+ -+ fprintf( stderr, "gamescope: invalid value for --bicubic\n" ); -+ exit(1); -+} -+ - static enum gamescope::GamescopeBackend parse_backend_name(const char *str) - { - if (strcmp(str, "auto") == 0) { -@@ -704,6 +730,9 @@ int main(int argc, char **argv) - case 'F': - g_wantedUpscaleFilter = parse_upscaler_filter(optarg); - break; -+ case 'D': -+ g_wantedDownscaleFilter = parse_downscaling_filter(optarg); -+ break; - case 'b': - g_bBorderlessOutputWindow = true; - break; -diff --git a/src/main.hpp b/src/main.hpp -index 2e6fb833a..f8cc03053 100644 ---- a/src/main.hpp -+++ b/src/main.hpp -@@ -40,6 +40,18 @@ enum class GamescopeUpscaleFilter : uint32_t - FROM_VIEW = 0xF, // internal - }; - -+enum class GamescopeDownscaleFilter : uint32_t -+{ -+ LINEAR = 0, -+ BICUBIC, -+}; -+ -+struct GamescopeBicubicParams -+{ -+ float b = 0.3f; -+ float c = 0.3f; -+}; -+ - static constexpr bool DoesHardwareSupportUpscaleFilter( GamescopeUpscaleFilter eFilter ) - { - // Could do nearest someday... AMDGPU DC supports custom tap placement to an extent. -@@ -57,10 +69,13 @@ enum class GamescopeUpscaleScaler : uint32_t - }; - - extern GamescopeUpscaleFilter g_upscaleFilter; -+extern GamescopeDownscaleFilter g_downscaleFilter; - extern GamescopeUpscaleScaler g_upscaleScaler; - extern GamescopeUpscaleFilter g_wantedUpscaleFilter; -+extern GamescopeDownscaleFilter g_wantedDownscaleFilter; - extern GamescopeUpscaleScaler g_wantedUpscaleScaler; - extern int g_upscaleFilterSharpness; -+extern GamescopeBicubicParams g_bicubicParams; - - extern bool g_bBorderlessOutputWindow; - -diff --git a/src/meson.build b/src/meson.build -index 5174de670..907277a23 100644 ---- a/src/meson.build -+++ b/src/meson.build -@@ -66,6 +66,7 @@ shader_src = [ - 'shaders/cs_composite_rcas.comp', - 'shaders/cs_easu.comp', - 'shaders/cs_easu_fp16.comp', -+ 'shaders/cs_bicubic.comp', - 'shaders/cs_gaussian_blur_horizontal.comp', - 'shaders/cs_nis.comp', - 'shaders/cs_nis_fp16.comp', -diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp -index d61fd73ea..45f45deba 100644 ---- a/src/rendervulkan.cpp -+++ b/src/rendervulkan.cpp -@@ -44,6 +44,7 @@ - #include "cs_composite_rcas.h" - #include "cs_easu.h" - #include "cs_easu_fp16.h" -+#include "cs_bicubic.h" - #include "cs_gaussian_blur_horizontal.h" - #include "cs_nis.h" - #include "cs_nis_fp16.h" -@@ -52,6 +53,7 @@ - #define A_CPU - #include "shaders/ffx_a.h" - #include "shaders/ffx_fsr1.h" -+#include "shaders/bicubic.h" - - #include "reshade_effect_manager.hpp" - -@@ -890,11 +892,13 @@ bool CVulkanDevice::createShaders() - if (m_bSupportsFp16) - { - SHADER(EASU, cs_easu_fp16); -+ SHADER(BICUBIC, cs_bicubic); - SHADER(NIS, cs_nis_fp16); - } - else - { - SHADER(EASU, cs_easu); -+ SHADER(BICUBIC, cs_bicubic); - SHADER(NIS, cs_nis); - } - SHADER(RGB_TO_NV12, cs_rgb_to_nv12); -@@ -1126,6 +1130,7 @@ void CVulkanDevice::compileAllPipelines() - SHADER(BLUR_FIRST_PASS, 1, 2, 1); - SHADER(RCAS, k_nMaxLayers, k_nMaxYcbcrMask_ToPreCompile, 1); - SHADER(EASU, 1, 1, 1); -+ SHADER(BICUBIC, 1, 1, 1); - SHADER(NIS, 1, 1, 1); - SHADER(RGB_TO_NV12, 1, 1, 1); - #undef SHADER -@@ -3691,6 +3696,17 @@ struct EasuPushData_t - } - }; - -+struct BicubicPushData_t -+{ -+ uvec4_t Const0; -+ uvec4_t Const1; -+ -+ BicubicPushData_t(float B, float C, uint32_t inputX, uint32_t inputY, uint32_t tempX, uint32_t tempY, uint32_t winX, uint32_t winY) -+ { -+ BicubicCon(&Const0.x, &Const1.x, B*10, C*10, inputX, inputY, tempX, tempY, winX, winY); -+ } -+}; -+ - struct RcasPushData_t - { - uvec2_t u_layer0Offset; -@@ -3900,7 +3916,53 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamesco - for (uint32_t i = 0; i < EOTF_Count; i++) - cmdBuffer->bindColorMgmtLuts(i, frameInfo->shaperLut[i], frameInfo->lut3D[i]); - -- if ( frameInfo->useFSRLayer0 ) -+ if ( frameInfo->useBICUBICLayer0 ) -+ { -+ // fprintf(stderr, "-- Use bicubic\n"); -+ uint32_t inputX = frameInfo->layers[0].tex->width(); -+ uint32_t inputY = frameInfo->layers[0].tex->height(); -+ -+ uint32_t tempX = frameInfo->layers[0].integerWidth(); -+ uint32_t tempY = frameInfo->layers[0].integerHeight(); -+ -+ update_tmp_images(tempX, tempY); -+ -+ cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_BICUBIC, frameInfo->layerCount, frameInfo->ycbcrMask())); -+ // cmdBuffer->bindTarget(compositeImage); -+ cmdBuffer->bindTarget(g_output.tmpOutput); -+ cmdBuffer->bindTexture(0, frameInfo->layers[0].tex); -+ cmdBuffer->setTextureSrgb(0, true); -+ cmdBuffer->setSamplerUnnormalized(0, false); -+ cmdBuffer->setSamplerNearest(0, false); -+ cmdBuffer->uploadConstants(g_bicubicParams.b -+ , g_bicubicParams.c -+ , inputX -+ , inputY -+ , tempX -+ , tempY -+ , currentOutputWidth -+ , currentOutputHeight -+ ); -+ -+ int pixelsPerGroup = 16; -+ -+ cmdBuffer->dispatch(div_roundup(tempX, pixelsPerGroup), div_roundup(tempY, pixelsPerGroup)); -+ -+ struct FrameInfo_t bicFrameInfo = *frameInfo; -+ bicFrameInfo.layers[0].tex = g_output.tmpOutput; -+ bicFrameInfo.layers[0].scale.x = 1.0f; -+ bicFrameInfo.layers[0].scale.y = 1.0f; -+ -+ cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_BLIT, bicFrameInfo.layerCount, bicFrameInfo.ycbcrMask())); -+ bind_all_layers(cmdBuffer.get(), &bicFrameInfo); -+ cmdBuffer->bindTarget(compositeImage); -+ cmdBuffer->uploadConstants(&bicFrameInfo); -+ -+ pixelsPerGroup = 8; -+ -+ cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup)); -+ } -+ else if ( frameInfo->useFSRLayer0 ) - { - uint32_t inputX = frameInfo->layers[0].tex->width(); - uint32_t inputY = frameInfo->layers[0].tex->height(); -diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp -index b967e849f..f6b80c5a1 100644 ---- a/src/rendervulkan.hpp -+++ b/src/rendervulkan.hpp -@@ -269,6 +269,7 @@ struct FrameInfo_t - { - bool useFSRLayer0; - bool useNISLayer0; -+ bool useBICUBICLayer0; - bool bFadingOut; - BlurMode blurLayer0; - int blurRadius; -@@ -530,6 +531,7 @@ enum ShaderType { - SHADER_TYPE_EASU, - SHADER_TYPE_RCAS, - SHADER_TYPE_NIS, -+ SHADER_TYPE_BICUBIC, - SHADER_TYPE_RGB_TO_NV12, - - SHADER_TYPE_COUNT -diff --git a/src/shaders/bicubic.h b/src/shaders/bicubic.h -new file mode 100644 -index 000000000..8117e8783 ---- /dev/null -+++ b/src/shaders/bicubic.h -@@ -0,0 +1,44 @@ -+//_____________________________________________________________/\_______________________________________________________________ -+//============================================================================================================================== -+// -+// -+// BICUBIC IMAGE SCALING -+// -+// -+//------------------------------------------------------------------------------------------------------------------------------ -+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -+//_____________________________________________________________/\_______________________________________________________________ -+//============================================================================================================================== -+// CONSTANT SETUP -+//============================================================================================================================== -+// Call to setup required constant values (works on CPU or GPU). -+A_STATIC void BicubicCon( -+outAU4 con0, -+outAU4 con1, -+// Configurable parameters -+AU1 B, -+AU1 C, -+// This the rendered image resolution -+AF1 inputRenderedSizeX, -+AF1 inputRenderedSizeY, -+// This is the resolution of the resource containing the input image (useful for dynamic resolution) -+AF1 inputCurrentSizeX, -+AF1 inputCurrentSizeY, -+// This is the window width / height -+AF1 outputTargetSizeX, -+AF1 outputTargetSizeY) -+{ -+ // Input/Output size information -+ con0[0]=AU1_AF1(inputRenderedSizeX); -+ con0[1]=AU1_AF1(inputRenderedSizeY); -+ con0[2]=AU1_AF1(inputCurrentSizeX); -+ con0[3]=AU1_AF1(inputCurrentSizeY); -+ -+ // Viewport pixel position to normalized image space. -+ con1[0]=AU1_AF1(outputTargetSizeX); -+ con1[1]=AU1_AF1(outputTargetSizeY); -+ con1[2]=B; -+ con1[3]=C; -+} -diff --git a/src/shaders/cs_bicubic.comp b/src/shaders/cs_bicubic.comp -new file mode 100644 -index 000000000..4904433b5 ---- /dev/null -+++ b/src/shaders/cs_bicubic.comp -@@ -0,0 +1,176 @@ -+// References -+// https://www.codeproject.com/Articles/236394/Bi-Cubic-and-Bi-Linear-Interpolation-with-GLSL -+// https://stackoverflow.com/questions/13501081/efficient-bicubic-filtering-code-in-glsl -+// https://web.archive.org/web/20180927181721/http://www.java-gaming.org/index.php?topic=35123.0 -+// https://gist.github.com/TheRealMJP/c83b8c0f46b63f3a88a5986f4fa982b1 -+ -+#version 460 -+ -+#extension GL_GOOGLE_include_directive : require -+#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require -+ -+#include "descriptor_set.h" -+ -+layout( -+ local_size_x = 64, -+ local_size_y = 1, -+ local_size_z = 1) in; -+ -+// Push constant is a mechanism in modern OpenGL that allows passing small amounts of frequently -+// updated data to the shader without needing to bind a buffer -+layout(push_constant) -+uniform layers_t { -+ uvec4 c0, c1; -+}; -+ -+#define A_GPU 1 -+#define A_GLSL 1 -+#define A_HALF 1 -+#include "ffx_a.h" -+#include "bicubic.h" -+ -+// The Mitchell–Netravali filters or BC-splines -+// https://en.wikipedia.org/wiki/Mitchell%E2%80%93Netravali_filters -+// Conditionals are slow in GPU code, so to represent 0 <= f < 1 and 1 <= f < 2 -+// the P(d) form shown in the wikipedia page is used -+vec4 mitchellNetravaliWeights(float f, float B, float C) -+{ -+ float w0 = ((12.0 - 9.0 * B - 6.0 * C) * pow(f, 3.0)) + -+ ((-18.0 + 12.0 * B + 6.0 * C) * pow(f, 2.0)) + -+ (6.0 - 2.0 * B); -+ -+ float w1 = ((-B - 6.0 * C) * pow(f - 1.0, 3.0)) + -+ ((6.0 * B + 30.0 * C) * pow(f - 1.0, 2.0)) + -+ ((-12.0 * B - 48.0 * C) * (f - 1.0)) + -+ (8.0 * B + 24.0 * C); -+ -+ float w2 = ((12.0 - 9.0 * B - 6.0 * C) * pow(1.0 - f, 3.0)) + -+ ((-18.0 + 12.0 * B + 6.0 * C) * pow(1.0 - f, 2.0)) + -+ (6.0 - 2.0 * B); -+ -+ float w3 = ((-B - 6.0 * C) * pow(2.0 - f, 3.0)) + -+ ((6.0 * B + 30.0 * C) * pow(2.0 - f, 2.0)) + -+ ((-12.0 * B - 48.0 * C) * (2.0 - f)) + -+ (8.0 * B + 24.0 * C); -+ -+ return vec4(w0, w1, w2, w3); -+} -+ -+// https://stackoverflow.com/questions/13501081/efficient-bicubic-filtering-code-in-glsl -+// https://web.archive.org/web/20180927181721/http://www.java-gaming.org/index.php?topic=35123.0 -+// This is an efficient method to implement bicubic filtering, it takes -+// advantage of the fact that the bilinear approach gives the weighted average -+// of a 2x2 area. -+vec4 textureBicubic(sampler2D splr, vec2 texCoords) -+{ -+ vec2 texSize = textureSize(splr, 0); -+ vec2 invTexSize = 1.0 / texSize; -+ -+ // Converts normalized coordinates into pixel-space coordinate -+ // Example: If texCoords is (0.5, 0.5), and the texture size is (1920, 1080), the result will be -+ // (960, 540)—the center of the texture in pixel space. -+ // Subtracting 0.5 ensures that you're sampling from the center of the texel rather than its corner -+ // Example: Assume we have a 3x3 texture and texCoords = (0.5, 0.5): -+ // [0,0][1,0][2,0] -+ // [0,1][1,1][2,1] -+ // [0,2][1,2][2,2] -+ // texCoords * texSize - 0.5 maps to (1.5, 1.5), which is between (1,1) and (2,2), then -+ // subtracts 0.5 to move it to (1.0, 1.0)—the center of the texel -+ texCoords = texCoords * texSize - 0.5; -+ -+ // Get B and C that were pushed from the user input (or default values) -+ float B = c1[2] / 10.0f; -+ float C = c1[3] / 10.0f; -+ -+ // Get the fractional part of the coordinates -+ // They are used in Mitchell Netravali's strategy to calculate the interpolation weights, -+ // i.e., how much influence the neighboring vertices have on the final pixel value -+ vec2 fxy = fract(texCoords); -+ texCoords -= fxy; -+ -+ // Calculate bicubic weights -+ // These weights represent how much influence each neighboring texel in the 4x4 grid will have -+ // on the final interpolated pixel value -+ vec4 xweights = mitchellNetravaliWeights(fxy.x, B, C); -+ vec4 yweights = mitchellNetravaliWeights(fxy.y, B, C); -+ -+ // Modify the current texture coordinates to have an offset in texels for each coordinate -+ // E.g. texCoords + vec(-1.0, 0.0) is a texel to the left -+ // texCoords + vec(1.0, 0.0) is a texel to the right -+ // texCoords + vec(0.0, 1.0) is a texel downwards -+ // texCoords + vec(0.0, -1.0) is a texel upwards -+ vec4 offsetTexels = texCoords.xxyy; -+ offsetTexels += vec2 (-1.0, +1.0).xyxy; -+ // Normalize weights to range between (0,1) -+ // vec4 sumWeights = vec4(xweights.xz + xweights.yw, yweights.xz + yweights.yw); -+ // vec4 normalizedWeights = vec4 (xweights.yw, yweights.yw) / sumWeights; -+ vec4 sumWeights = vec4(xweights.x + xweights.y, xweights.z + xweights.w, yweights.x + yweights.y, yweights.z + yweights.w); -+ vec4 normalizedWeights = vec4 (xweights.y, xweights.w, yweights.y, yweights.w) / sumWeights; -+ // Use the weights to influence the sampling position inside each texel -+ // Each texel has a size from (0,1) -+ vec4 offsetSampler = offsetTexels + normalizedWeights; -+ // Go back to normalized space -+ offsetSampler *= invTexSize.xxyy; -+ // Perform the sampling -+ vec4 sample0 = texture(splr, offsetSampler.xz); -+ vec4 sample1 = texture(splr, offsetSampler.yz); -+ vec4 sample2 = texture(splr, offsetSampler.xw); -+ vec4 sample3 = texture(splr, offsetSampler.yw); -+ -+ // Now we perform linear interpolation in the selected points -+ // The mix(a, b, t) function in GLSL performs linear interpolation between a and b based on the -+ // parameter t, t is between 0 and 1 -+ // https://registry.khronos.org/OpenGL-Refpages/gl4/html/mix.xhtml -+ -+ // Here we want to normalize sx and sy to between 0 and 1 (t value) -+ float sx = sumWeights.x / (sumWeights.x + sumWeights.y); -+ float sy = sumWeights.z / (sumWeights.z + sumWeights.w); -+ -+ return mix( -+ mix(sample3, sample2, sx), mix(sample1, sample0, sx) -+ , sy); -+} -+ -+void bicPass(uvec2 pos) -+{ -+ // Retrieve pushed values -+ AF2 inputRenderedSize = AF2_AU2(c0.xy); -+ AF2 inputCurrentSize = AF2_AU2(c0.zw); -+ AF2 outputTargetSize = AF2_AU2(c1.xy); -+ -+ // ARcpF1(x) == 1.0 / x -+ // scaleFactor is the division between the rendered image and the size it should have at the end -+ // E.g.: Rendered 1920x1080, window size is 960x540, then scaleFactor is 2x2 -+ AF2 scaleFactor = inputRenderedSize * vec2(ARcpF1(inputCurrentSize.x), ARcpF1(inputCurrentSize.y)); -+ -+ // The parameter pos of this function is used to iterate over the output image (e.g. 960x540) -+ // The position of the processed pixel should be taken from the rendered image (e.g. 1920x1080) -+ // 10x10 in the output, corresponds to 20x20 in the original image -+ AF2 positionPixel=AF2(pos)*scaleFactor; -+ -+ // Normalize the image space to be between [0,1] -+ positionPixel=positionPixel*vec2(ARcpF1(inputRenderedSize.x),ARcpF1(inputRenderedSize.y)); -+ -+ // Apply the bicubic algorithm in the normalized pixel position -+ vec4 bicPass = textureBicubic(s_samplers[0], positionPixel); -+ -+ imageStore(dst, ivec2(pos), bicPass); -+} -+ -+ -+void main() -+{ -+ // AMD recommends to use this swizzle and to process 4 pixel per invocation -+ // for better cache utilisation -+ uvec2 pos = ARmp8x8(gl_LocalInvocationID.x) + uvec2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u); -+ -+ bicPass(pos); -+ pos.x += 8u; -+ bicPass(pos); -+ pos.y += 8u; -+ bicPass(pos); -+ pos.x -= 8u; -+ bicPass(pos); -+} -+ -+/* vim: set expandtab ft=cpp fdm=marker ts=4 sw=4 tw=100 et :*/ -diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index 02148f423..b33608707 100644 ---- a/src/steamcompmgr.cpp -+++ b/src/steamcompmgr.cpp -@@ -2356,6 +2356,10 @@ paint_all(bool async) - paint_window(w, w, &frameInfo, global_focus.cursor, PaintWindowFlag::BasePlane | PaintWindowFlag::DrawBorders, 1.0f, override); - - bool needsScaling = frameInfo.layers[0].scale.x < 0.999f && frameInfo.layers[0].scale.y < 0.999f; -+ // Temporarily allow upscaling as well -+ // bool needsDownScaling = frameInfo.layers[0].scale.x > 1.001f && frameInfo.layers[0].scale.y > 1.001f; -+ bool needsDownScaling = true; -+ frameInfo.useBICUBICLayer0 = g_downscaleFilter == GamescopeDownscaleFilter::BICUBIC && needsDownScaling; - frameInfo.useFSRLayer0 = g_upscaleFilter == GamescopeUpscaleFilter::FSR && needsScaling; - frameInfo.useNISLayer0 = g_upscaleFilter == GamescopeUpscaleFilter::NIS && needsScaling; - } -@@ -2488,6 +2492,7 @@ paint_all(bool async) - } - - frameInfo.useFSRLayer0 = false; -+ frameInfo.useBICUBICLayer0 = false; - frameInfo.useNISLayer0 = false; - } - -@@ -7846,6 +7851,7 @@ steamcompmgr_main(int argc, char **argv) - g_bSteamIsActiveWindow = false; - g_upscaleScaler = g_wantedUpscaleScaler; - g_upscaleFilter = g_wantedUpscaleFilter; -+ g_downscaleFilter = g_wantedDownscaleFilter; - } - - // If we're in the middle of a fade, then keep us - -From 6acfb50f09cc9fd8a16dd502d260b920a8ba02cb Mon Sep 17 00:00:00 2001 -From: "Ruan E. Formigoni" -Date: Sat, 5 Oct 2024 15:19:23 -0300 -Subject: [PATCH 2/9] Partial bicubic code adaptation [2] - ---- - src/Backends/DRMBackend.cpp | 1 + - src/Backends/OpenVRBackend.cpp | 1 + - src/main.cpp | 6 +++--- - src/rendervulkan.cpp | 3 +-- - src/shaders/descriptor_set.h | 1 + - src/steamcompmgr.cpp | 17 +++++++++++++++++ - src/steamcompmgr.hpp | 1 + - src/xwayland_ctx.hpp | 1 + - 8 files changed, 26 insertions(+), 5 deletions(-) - -diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp -index c62635f19..76c9f0514 100644 ---- a/src/Backends/DRMBackend.cpp -+++ b/src/Backends/DRMBackend.cpp -@@ -3211,6 +3211,7 @@ namespace gamescope - bNeedsFullComposite |= bWasFirstFrame; - bNeedsFullComposite |= pFrameInfo->useFSRLayer0; - bNeedsFullComposite |= pFrameInfo->useNISLayer0; -+ bNeedsFullComposite |= pFrameInfo->useBICUBICLayer0; - bNeedsFullComposite |= pFrameInfo->blurLayer0; - bNeedsFullComposite |= bNeedsCompositeFromFilter; - bNeedsFullComposite |= !k_bUseCursorPlane && bDrewCursor; -diff --git a/src/Backends/OpenVRBackend.cpp b/src/Backends/OpenVRBackend.cpp -index c39caa54b..08640f853 100644 ---- a/src/Backends/OpenVRBackend.cpp -+++ b/src/Backends/OpenVRBackend.cpp -@@ -554,6 +554,7 @@ namespace gamescope - bNeedsFullComposite |= cv_composite_force; - bNeedsFullComposite |= pFrameInfo->useFSRLayer0; - bNeedsFullComposite |= pFrameInfo->useNISLayer0; -+ bNeedsFullComposite |= pFrameInfo->useBICUBICLayer0; - bNeedsFullComposite |= pFrameInfo->blurLayer0; - bNeedsFullComposite |= bNeedsCompositeFromFilter; - bNeedsFullComposite |= g_bColorSliderInUse; -diff --git a/src/main.cpp b/src/main.cpp -index ed7fd4fa7..de25ddeab 100644 ---- a/src/main.cpp -+++ b/src/main.cpp -@@ -402,7 +402,7 @@ static enum GamescopeUpscaleFilter parse_upscaler_filter(const char *str) - } - } - --static enum GamescopeDownscaleFilter parse_downscaling_filter(const char *str) -+static enum GamescopeDownscaleFilter parse_downscaler_filter(const char *str) - { - std::stringstream ss{optarg}; - -@@ -415,7 +415,7 @@ static enum GamescopeDownscaleFilter parse_downscaling_filter(const char *str) - // Ovewrite default global parameters - g_bicubicParams.b = b; - g_bicubicParams.c = c; -- // Set downscaling filters -+ // Set downscaler filters - return GamescopeDownscaleFilter::BICUBIC; - } - -@@ -731,7 +731,7 @@ int main(int argc, char **argv) - g_wantedUpscaleFilter = parse_upscaler_filter(optarg); - break; - case 'D': -- g_wantedDownscaleFilter = parse_downscaling_filter(optarg); -+ g_wantedDownscaleFilter = parse_downscaler_filter(optarg); - break; - case 'b': - g_bBorderlessOutputWindow = true; -diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp -index 45f45deba..e80afb68a 100644 ---- a/src/rendervulkan.cpp -+++ b/src/rendervulkan.cpp -@@ -889,16 +889,15 @@ bool CVulkanDevice::createShaders() - SHADER(BLUR_COND, cs_composite_blur_cond); - SHADER(BLUR_FIRST_PASS, cs_gaussian_blur_horizontal); - SHADER(RCAS, cs_composite_rcas); -+ SHADER(BICUBIC, cs_bicubic); - if (m_bSupportsFp16) - { - SHADER(EASU, cs_easu_fp16); -- SHADER(BICUBIC, cs_bicubic); - SHADER(NIS, cs_nis_fp16); - } - else - { - SHADER(EASU, cs_easu); -- SHADER(BICUBIC, cs_bicubic); - SHADER(NIS, cs_nis); - } - SHADER(RGB_TO_NV12, cs_rgb_to_nv12); -diff --git a/src/shaders/descriptor_set.h b/src/shaders/descriptor_set.h -index f2b8527c8..64cc1c9c3 100644 ---- a/src/shaders/descriptor_set.h -+++ b/src/shaders/descriptor_set.h -@@ -21,6 +21,7 @@ const int filter_nearest = 1; - const int filter_fsr = 2; - const int filter_nis = 3; - const int filter_pixel = 4; -+const int filter_bicubic = 5; - const int filter_from_view = 255; - - const int EOTF_Gamma22 = 0; -diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index b33608707..86a815f06 100644 ---- a/src/steamcompmgr.cpp -+++ b/src/steamcompmgr.cpp -@@ -874,6 +874,7 @@ gamescope::ConCommand cc_debug_set_fps_limit( "debug_set_fps_limit", "Set refres - static int g_nRuntimeInfoFd = -1; - - bool g_bFSRActive = false; -+bool g_bBicubicActive = false; - - BlurMode g_BlurMode = BLUR_MODE_OFF; - BlurMode g_BlurModeOld = BLUR_MODE_OFF; -@@ -2497,6 +2498,7 @@ paint_all(bool async) - } - - g_bFSRActive = frameInfo.useFSRLayer0; -+ g_bBicubicActive = frameInfo.useBICUBICLayer0; - - g_bFirstFrame = false; - -@@ -5417,6 +5419,9 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) - g_wantedUpscaleScaler = GamescopeUpscaleScaler::AUTO; - g_wantedUpscaleFilter = GamescopeUpscaleFilter::NIS; - break; -+ case 5: -+ g_wantedDownscaleFilter = GamescopeDownscaleFilter::BICUBIC; -+ break; - } - hasRepaint = true; - } -@@ -6969,6 +6974,7 @@ void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_ - ctx->atoms.gamescopeLowLatency = XInternAtom( ctx->dpy, "GAMESCOPE_LOW_LATENCY", false ); - - ctx->atoms.gamescopeFSRFeedback = XInternAtom( ctx->dpy, "GAMESCOPE_FSR_FEEDBACK", false ); -+ ctx->atoms.gamescopeBicubicFeedback = XInternAtom( ctx->dpy, "GAMESCOPE_BICUBIC_FEEDBACK", false ); - - ctx->atoms.gamescopeBlurMode = XInternAtom( ctx->dpy, "GAMESCOPE_BLUR_MODE", false ); - ctx->atoms.gamescopeBlurRadius = XInternAtom( ctx->dpy, "GAMESCOPE_BLUR_RADIUS", false ); -@@ -7224,6 +7230,7 @@ extern int g_nPreferredOutputWidth; - extern int g_nPreferredOutputHeight; - - static bool g_bWasFSRActive = false; -+static bool g_bWasBicubicActive = false; - - bool g_bAppWantsHDRCached = false; - -@@ -7615,6 +7622,16 @@ steamcompmgr_main(int argc, char **argv) - flush_root = true; - } - -+ if ( g_bBicubicActive != g_bWasBicubicActive ) -+ { -+ uint32_t active = g_bBicubicActive ? 1 : 0; -+ XChangeProperty( root_ctx->dpy, root_ctx->root, root_ctx->atoms.gamescopeBicubicFeedback, XA_CARDINAL, 32, PropModeReplace, -+ (unsigned char *)&active, 1 ); -+ -+ g_bWasBicubicActive = g_bBicubicActive; -+ flush_root = true; -+ } -+ - if (global_focus.IsDirty()) - determine_and_apply_focus(); - -diff --git a/src/steamcompmgr.hpp b/src/steamcompmgr.hpp -index 9f384c461..ea0746860 100644 ---- a/src/steamcompmgr.hpp -+++ b/src/steamcompmgr.hpp -@@ -128,6 +128,7 @@ extern float focusedWindowOffsetX; - extern float focusedWindowOffsetY; - - extern bool g_bFSRActive; -+extern bool g_bBicubicActive; - - extern uint32_t inputCounter; - extern uint64_t g_lastWinSeq; -diff --git a/src/xwayland_ctx.hpp b/src/xwayland_ctx.hpp -index df2af70d1..e212a119e 100644 ---- a/src/xwayland_ctx.hpp -+++ b/src/xwayland_ctx.hpp -@@ -164,6 +164,7 @@ struct xwayland_ctx_t final : public gamescope::IWaitable - Atom gamescopeLowLatency; - - Atom gamescopeFSRFeedback; -+ Atom gamescopeBicubicFeedback; - - Atom gamescopeBlurMode; - Atom gamescopeBlurRadius; - -From fceb9b90021e0abf2da6e8cff71034dc204afb24 Mon Sep 17 00:00:00 2001 -From: "Ruan E. Formigoni" -Date: Mon, 7 Oct 2024 00:37:49 -0300 -Subject: [PATCH 3/9] Fix indentation - ---- - src/Backends/OpenVRBackend.cpp | 2 +- - src/Backends/WaylandBackend.cpp | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/Backends/OpenVRBackend.cpp b/src/Backends/OpenVRBackend.cpp -index 08640f853..96a3d0166 100644 ---- a/src/Backends/OpenVRBackend.cpp -+++ b/src/Backends/OpenVRBackend.cpp -@@ -554,7 +554,7 @@ namespace gamescope - bNeedsFullComposite |= cv_composite_force; - bNeedsFullComposite |= pFrameInfo->useFSRLayer0; - bNeedsFullComposite |= pFrameInfo->useNISLayer0; -- bNeedsFullComposite |= pFrameInfo->useBICUBICLayer0; -+ bNeedsFullComposite |= pFrameInfo->useBICUBICLayer0; - bNeedsFullComposite |= pFrameInfo->blurLayer0; - bNeedsFullComposite |= bNeedsCompositeFromFilter; - bNeedsFullComposite |= g_bColorSliderInUse; -diff --git a/src/Backends/WaylandBackend.cpp b/src/Backends/WaylandBackend.cpp -index d0abb2fa2..d9d213e85 100644 ---- a/src/Backends/WaylandBackend.cpp -+++ b/src/Backends/WaylandBackend.cpp -@@ -1598,7 +1598,7 @@ namespace gamescope - bNeedsFullComposite |= cv_composite_force; - bNeedsFullComposite |= pFrameInfo->useFSRLayer0; - bNeedsFullComposite |= pFrameInfo->useNISLayer0; -- bNeedsFullComposite |= pFrameInfo->useBICUBICLayer0; -+ bNeedsFullComposite |= pFrameInfo->useBICUBICLayer0; - bNeedsFullComposite |= pFrameInfo->blurLayer0; - bNeedsFullComposite |= bNeedsCompositeFromFilter; - bNeedsFullComposite |= g_bColorSliderInUse; - -From 514c7c243730e6f4cae719cd5a662bc5ee68dc6c Mon Sep 17 00:00:00 2001 -From: "Ruan E. Formigoni" -Date: Mon, 7 Oct 2024 00:43:10 -0300 -Subject: [PATCH 4/9] Fix black screen on bicubic filter - ---- - src/shaders/cs_bicubic.comp | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/src/shaders/cs_bicubic.comp b/src/shaders/cs_bicubic.comp -index 4904433b5..2b6dfb824 100644 ---- a/src/shaders/cs_bicubic.comp -+++ b/src/shaders/cs_bicubic.comp -@@ -8,6 +8,7 @@ - - #extension GL_GOOGLE_include_directive : require - #extension GL_EXT_shader_explicit_arithmetic_types_float16 : require -+#extension GL_EXT_scalar_block_layout : require - - #include "descriptor_set.h" - -@@ -18,7 +19,7 @@ layout( - - // Push constant is a mechanism in modern OpenGL that allows passing small amounts of frequently - // updated data to the shader without needing to bind a buffer --layout(push_constant) -+layout(binding = 0, scalar) - uniform layers_t { - uvec4 c0, c1; - }; - -From 8fa17e48fa07210f03d2643f3fe8811e2e6aa9a4 Mon Sep 17 00:00:00 2001 -From: "Ruan E. Formigoni" -Date: Mon, 7 Oct 2024 01:27:35 -0300 -Subject: [PATCH 5/9] Use bicubic as an option of --filter - ---- - src/main.cpp | 39 ++++++++++++++++++++++++++++++++------- - 1 file changed, 32 insertions(+), 7 deletions(-) - -diff --git a/src/main.cpp b/src/main.cpp -index de25ddeab..b7f5985b9 100644 ---- a/src/main.cpp -+++ b/src/main.cpp -@@ -64,7 +64,6 @@ const struct option *gamescope_options = (struct option[]){ - { "output-height", required_argument, nullptr, 'H' }, - { "sharpness", required_argument, nullptr, 0 }, - { "fsr-sharpness", required_argument, nullptr, 0 }, -- { "bicubic", optional_argument, nullptr, 'D' }, - { "rt", no_argument, nullptr, 0 }, - { "prefer-vk-device", required_argument, 0 }, - { "expose-wayland", no_argument, 0 }, -@@ -404,8 +403,26 @@ static enum GamescopeUpscaleFilter parse_upscaler_filter(const char *str) - - static enum GamescopeDownscaleFilter parse_downscaler_filter(const char *str) - { -- std::stringstream ss{optarg}; -+ std::string_view arg{str}; - -+ // If the string is just 'bicubic' use default values -+ if ( arg == "bicubic" ) { -+ return GamescopeDownscaleFilter::BICUBIC; -+ } -+ -+ // Arguments start after ':' -+ if ( auto search = arg.find(':'); search == std::string::npos ) { -+ fprintf( stderr, "gamescope: invalid argument for --filter=bicubic:float,float\n" ); -+ exit(1); -+ } else { -+ arg = std::string_view(arg.data() + search + 1); -+ } -+ -+ // Push arguments to stream -+ std::stringstream ss; -+ ss << arg; -+ -+ // Validate arguments from stream - double b, c; - char comma; - if ((ss >> b >> comma >> c) && (comma == ',')) { -@@ -419,10 +436,21 @@ static enum GamescopeDownscaleFilter parse_downscaler_filter(const char *str) - return GamescopeDownscaleFilter::BICUBIC; - } - -- fprintf( stderr, "gamescope: invalid value for --bicubic\n" ); -+ fprintf( stderr, "gamescope: invalid value for --filter\n" ); - exit(1); - } - -+static void parse_filter(const char *str) -+{ -+ fprintf(stderr, "str: %s\n", str); -+ if (std::string_view{str}.starts_with("bicubic")) { -+ fprintf(stderr, "Startswith\n"); -+ g_wantedDownscaleFilter = parse_downscaler_filter(str); -+ } else { -+ g_wantedUpscaleFilter = parse_upscaler_filter(str); -+ } -+} -+ - static enum gamescope::GamescopeBackend parse_backend_name(const char *str) - { - if (strcmp(str, "auto") == 0) { -@@ -728,10 +756,7 @@ int main(int argc, char **argv) - g_wantedUpscaleScaler = parse_upscaler_scaler(optarg); - break; - case 'F': -- g_wantedUpscaleFilter = parse_upscaler_filter(optarg); -- break; -- case 'D': -- g_wantedDownscaleFilter = parse_downscaler_filter(optarg); -+ parse_filter(optarg); - break; - case 'b': - g_bBorderlessOutputWindow = true; - -From 5e50b2f931abe251e656138ba7bd0e5694dcc671 Mon Sep 17 00:00:00 2001 -From: "Ruan E. Formigoni" -Date: Mon, 7 Oct 2024 01:44:06 -0300 -Subject: [PATCH 6/9] Updated README with bicubic info - ---- - README.md | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/README.md b/README.md -index 8aa0227bf..cfae48017 100644 ---- a/README.md -+++ b/README.md -@@ -66,6 +66,7 @@ See `gamescope --help` for a full list of options. - * `-o`: set a frame-rate limit for the game when unfocused. Specified in frames per second. Defaults to unlimited. - * `-F fsr`: use AMD FidelityFX™ Super Resolution 1.0 for upscaling - * `-F nis`: use NVIDIA Image Scaling v1.0.3 for upscaling -+* `-F bicubic`: use a bicubic filter for downscaling - * `-S integer`: use integer scaling. - * `-S stretch`: use stretch scaling, the game will fill the window. (e.g. 4:3 to 16:9) - * `-b`: create a border-less window. - -From c1832fc5a44df8fc4c30c47b45638694e51bfa53 Mon Sep 17 00:00:00 2001 -From: "Ruan E. Formigoni" -Date: Mon, 7 Oct 2024 01:45:55 -0300 -Subject: [PATCH 7/9] Fix indentation in meson.build - ---- - src/meson.build | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/meson.build b/src/meson.build -index 907277a23..90096979a 100644 ---- a/src/meson.build -+++ b/src/meson.build -@@ -66,7 +66,7 @@ shader_src = [ - 'shaders/cs_composite_rcas.comp', - 'shaders/cs_easu.comp', - 'shaders/cs_easu_fp16.comp', -- 'shaders/cs_bicubic.comp', -+ 'shaders/cs_bicubic.comp', - 'shaders/cs_gaussian_blur_horizontal.comp', - 'shaders/cs_nis.comp', - 'shaders/cs_nis_fp16.comp', - -From 7ad3fc2a5e0ce35758fec6a8fb51fb4306cbc6e1 Mon Sep 17 00:00:00 2001 -From: "Ruan E. Formigoni" -Date: Mon, 7 Oct 2024 05:31:33 -0300 -Subject: [PATCH 8/9] Remove debug print - ---- - src/main.cpp | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/src/main.cpp b/src/main.cpp -index b7f5985b9..32a20cbd5 100644 ---- a/src/main.cpp -+++ b/src/main.cpp -@@ -442,9 +442,7 @@ static enum GamescopeDownscaleFilter parse_downscaler_filter(const char *str) - - static void parse_filter(const char *str) - { -- fprintf(stderr, "str: %s\n", str); - if (std::string_view{str}.starts_with("bicubic")) { -- fprintf(stderr, "Startswith\n"); - g_wantedDownscaleFilter = parse_downscaler_filter(str); - } else { - g_wantedUpscaleFilter = parse_upscaler_filter(str); - -From 6649fd8f0e7d2bce465855221f30eafc06f9a4ce Mon Sep 17 00:00:00 2001 -From: "Ruan E. Formigoni" -Date: Mon, 7 Oct 2024 05:34:15 -0300 -Subject: [PATCH 9/9] Remove outdated comments - ---- - src/rendervulkan.cpp | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp -index e80afb68a..33f999101 100644 ---- a/src/rendervulkan.cpp -+++ b/src/rendervulkan.cpp -@@ -3917,7 +3917,6 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamesco - - if ( frameInfo->useBICUBICLayer0 ) - { -- // fprintf(stderr, "-- Use bicubic\n"); - uint32_t inputX = frameInfo->layers[0].tex->width(); - uint32_t inputY = frameInfo->layers[0].tex->height(); - -@@ -3927,7 +3926,6 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamesco - update_tmp_images(tempX, tempY); - - cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_BICUBIC, frameInfo->layerCount, frameInfo->ycbcrMask())); -- // cmdBuffer->bindTarget(compositeImage); - cmdBuffer->bindTarget(g_output.tmpOutput); - cmdBuffer->bindTexture(0, frameInfo->layers[0].tex); - cmdBuffer->setTextureSrgb(0, true); diff --git a/spec_files/gamescope/gamescope.spec b/spec_files/gamescope/gamescope.spec index 45e5c905..57812371 100644 --- a/spec_files/gamescope/gamescope.spec +++ b/spec_files/gamescope/gamescope.spec @@ -3,12 +3,12 @@ %global _default_patch_fuzz 2 %global build_timestamp %(date +"%Y%m%d") #global gamescope_tag 3.15.11 -%global gamescope_commit d3174928d47f7e353e7daca63cf882d65660cc7c +%global gamescope_commit 4da5e4a37560f9b3c85af2679330f9ec292c8ee1 %define short_commit %(echo %{gamescope_commit} | cut -c1-8) Name: gamescope #Version: 100.%{gamescope_tag} -Version: 102.%{short_commit} +Version: 103.%{short_commit} Release: 1.bazzite Summary: Micro-compositor for video games on Wayland @@ -24,12 +24,6 @@ Patch0: 0001-cstdint.patch # https://github.com/ChimeraOS/gamescope Patch1: handheld.patch -# https://github.com/ValveSoftware/gamescope/pull/740 -Patch2: 740.patch - -# https://github.com/ValveSoftware/gamescope/pull/1671 -Patch3: 1671.patch - BuildRequires: meson >= 0.54.0 BuildRequires: ninja-build BuildRequires: cmake diff --git a/spec_files/gamescope/handheld.patch b/spec_files/gamescope/handheld.patch index 372021cd..a16ce19f 100644 --- a/spec_files/gamescope/handheld.patch +++ b/spec_files/gamescope/handheld.patch @@ -4,19 +4,10 @@ Date: Fri, 22 Nov 2024 01:37:48 +0100 Subject: [NA] add dev script --- - subprojects/edid-decode | 1 + - sync.sh | 21 +++++++++++++++++++++ - 2 files changed, 22 insertions(+) - create mode 160000 subprojects/edid-decode + sync.sh | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) create mode 100755 sync.sh -diff --git a/subprojects/edid-decode b/subprojects/edid-decode -new file mode 160000 -index 0000000..c6b859d ---- /dev/null -+++ b/subprojects/edid-decode -@@ -0,0 +1 @@ -+Subproject commit c6b859d7f0251e2433fb81bd3f67bd2011c2036c diff --git a/sync.sh b/sync.sh new file mode 100755 index 0000000..878bf6c @@ -45,7 +36,7 @@ index 0000000..878bf6c + # sudo reboot +EOF -- -2.47.0 +2.47.1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 @@ -164,7 +155,7 @@ index 2e6fb83..390c04a 100644 enum class GamescopeUpscaleFilter : uint32_t { -- -2.47.0 +2.47.1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 @@ -180,7 +171,7 @@ This reverts commit 299bc3410dcfd46da5e3c988354b60ed3a356900. 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index 11a7cad..df7616d 100644 +index 18be94d..cd7e8ac 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -3299,7 +3299,7 @@ found:; @@ -192,7 +183,7 @@ index 11a7cad..df7616d 100644 // Always update X's idea of focus, but still dirty // the it being outdated so we can resolve that globally later. -@@ -6044,28 +6044,37 @@ bool handle_done_commit( steamcompmgr_win_t *w, xwayland_ctx_t *ctx, uint64_t co +@@ -6050,28 +6050,37 @@ bool handle_done_commit( steamcompmgr_win_t *w, xwayland_ctx_t *ctx, uint64_t co // Window just got a new available commit, determine if that's worth a repaint // If this is an overlay that we're presenting, repaint @@ -257,7 +248,7 @@ index 095694e..e41fad9 100644 bool hasHwndStyle = false; uint32_t hwndStyle = 0; -- -2.47.0 +2.47.1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 @@ -318,7 +309,7 @@ index 78a86ee..99df8aa 100644 wlserver_keyboardfocus( old_kb_surf, false ); return; -- -2.47.0 +2.47.1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 @@ -357,7 +348,7 @@ index 8381889..a76b51b 100644 " --prefer-vk-device prefer Vulkan device for compositing (ex: 1002:7300)\n" " --force-orientation rotate the internal display (left, right, normal, upsidedown)\n" diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index df7616d..4a17499 100644 +index cd7e8ac..3f3d499 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -197,6 +197,7 @@ update_runtime_info(); @@ -377,7 +368,7 @@ index df7616d..4a17499 100644 { gamescope::cv_touch_click_mode = (gamescope::TouchClickMode) get_prop(ctx, ctx->root, ctx->atoms.steamTouchClickModeAtom, 0u ); } -@@ -7476,6 +7477,8 @@ steamcompmgr_main(int argc, char **argv) +@@ -7482,6 +7483,8 @@ steamcompmgr_main(int argc, char **argv) g_reshade_technique_idx = atoi(optarg); } else if (strcmp(opt_name, "mura-map") == 0) { set_mura_overlay(optarg); @@ -387,7 +378,7 @@ index df7616d..4a17499 100644 break; case '?': -- -2.47.0 +2.47.1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 @@ -422,7 +413,7 @@ index a76b51b..84e05a9 100644 " --xwayland-count create N xwayland servers\n" " --prefer-vk-device prefer Vulkan device for compositing (ex: 1002:7300)\n" diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index 4a17499..da3115f 100644 +index 3f3d499..5345b55 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -147,6 +147,7 @@ static lut3d_t g_tmpLut3d; @@ -442,7 +433,7 @@ index 4a17499..da3115f 100644 { auto tex = vulkan_get_hacky_blank_texture(); if ( tex != nullptr ) -@@ -7479,6 +7480,8 @@ steamcompmgr_main(int argc, char **argv) +@@ -7485,6 +7486,8 @@ steamcompmgr_main(int argc, char **argv) set_mura_overlay(optarg); } else if (strcmp(opt_name, "disable-touch-click") == 0) { cv_disable_touch_click = true; @@ -452,7 +443,7 @@ index 4a17499..da3115f 100644 break; case '?': -- -2.47.0 +2.47.1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 @@ -488,7 +479,7 @@ index 84e05a9..2398535 100644 } else if (strcmp(opt_name, "custom-refresh-rates") == 0) { g_customRefreshRates = parse_custom_refresh_rates( optarg ); -- -2.47.0 +2.47.1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 @@ -520,7 +511,7 @@ index 99df8aa..5e8f516 100644 default: case GAMESCOPE_PANEL_ORIENTATION_AUTO: -- -2.47.0 +2.47.1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 @@ -558,7 +549,7 @@ index 2398535..0621c65 100644 " --prefer-vk-device prefer Vulkan device for compositing (ex: 1002:7300)\n" " --force-orientation rotate the internal display (left, right, normal, upsidedown)\n" diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index da3115f..69fd348 100644 +index 5345b55..6544cf0 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -148,6 +148,7 @@ extern int g_nDynamicRefreshHz; @@ -587,7 +578,7 @@ index da3115f..69fd348 100644 return false; if ( g_bChangeDynamicRefreshBasedOnGameOpenRatherThanActive ) -@@ -7480,6 +7481,8 @@ steamcompmgr_main(int argc, char **argv) +@@ -7486,6 +7487,8 @@ steamcompmgr_main(int argc, char **argv) set_mura_overlay(optarg); } else if (strcmp(opt_name, "disable-touch-click") == 0) { cv_disable_touch_click = true; @@ -597,7 +588,7 @@ index da3115f..69fd348 100644 g_bHackyEnabled = true; } -- -2.47.0 +2.47.1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 @@ -659,10 +650,10 @@ index 0621c65..056e1c1 100644 g_customRefreshRates = parse_custom_refresh_rates( optarg ); } else if (strcmp(opt_name, "sharpness") == 0 || diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index 69fd348..3dd64f8 100644 +index 6544cf0..d541f70 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp -@@ -7192,7 +7192,7 @@ void update_mode_atoms(xwayland_ctx_t *root_ctx, bool* needs_flush = nullptr) +@@ -7198,7 +7198,7 @@ void update_mode_atoms(xwayland_ctx_t *root_ctx, bool* needs_flush = nullptr) if (needs_flush) *needs_flush = true; @@ -709,7 +700,7 @@ index 0569472..104f7a2 100644 std::vector wlserver_xdg_commit_queue(); -- -2.47.0 +2.47.1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 @@ -740,7 +731,7 @@ index 75c3258..f014be9 100644 m_Mutable.ValidDynamicRefreshRates = TableToVector( *otDynamicRefreshRates ); -- -2.47.0 +2.47.1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 @@ -755,7 +746,7 @@ disable VRR and use the classic frame limiter. 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index 3dd64f8..7dacfe7 100644 +index d541f70..8e5ad6a 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -165,6 +165,7 @@ uint32_t g_reshade_technique_idx = 0; @@ -810,7 +801,7 @@ index 3dd64f8..7dacfe7 100644 if ( ev->atom == ctx->atoms.gamescopeDisplayForceInternal ) { -- -2.47.0 +2.47.1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 @@ -824,7 +815,7 @@ Subject: fix(battery): run at half hz while at steamUI and disable VRR V2 + 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index 7dacfe7..4098c44 100644 +index 8e5ad6a..bc238e4 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -166,6 +166,9 @@ uint32_t g_reshade_technique_idx = 0; @@ -930,7 +921,7 @@ index 7dacfe7..4098c44 100644 } if ( ev->atom == ctx->atoms.gamescopeDisplayForceInternal ) { -@@ -7628,6 +7632,23 @@ steamcompmgr_main(int argc, char **argv) +@@ -7634,6 +7638,23 @@ steamcompmgr_main(int argc, char **argv) // as a question. const bool bIsVBlankFromTimer = vblank; @@ -955,7 +946,7 @@ index 7dacfe7..4098c44 100644 const bool bVRR = GetBackend()->IsVRRActive(); if ( bVRR ) -- -2.47.0 +2.47.1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 @@ -969,7 +960,7 @@ Subject: feat(battery): add atom for controlling frame halving 2 files changed, 8 insertions(+) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index 4098c44..6c8ce74 100644 +index bc238e4..d968de5 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -5919,6 +5919,10 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) @@ -983,7 +974,7 @@ index 4098c44..6c8ce74 100644 } static int -@@ -7089,6 +7093,8 @@ void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_ +@@ -7095,6 +7099,8 @@ void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_ ctx->atoms.primarySelection = XInternAtom(ctx->dpy, "PRIMARY", false); ctx->atoms.targets = XInternAtom(ctx->dpy, "TARGETS", false); @@ -1006,7 +997,7 @@ index df2af70..e4eec9f 100644 bool HasQueuedEvents(); -- -2.47.0 +2.47.1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 @@ -1077,7 +1068,7 @@ index f014be9..6bb0b88 100644 if ( bNeedsFullComposite ) diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp -index b537170..ccabd88 100644 +index d8a24e9..00f5ece 100644 --- a/src/rendervulkan.hpp +++ b/src/rendervulkan.hpp @@ -281,6 +281,8 @@ struct FrameInfo_t @@ -1090,7 +1081,7 @@ index b537170..ccabd88 100644 struct Layer_t { diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp -index 6c8ce74..dfee904 100644 +index d968de5..86773db 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -169,6 +169,8 @@ bool g_bVRRRequested = false; @@ -1130,7 +1121,7 @@ index 6c8ce74..dfee904 100644 } static int -@@ -7094,6 +7101,7 @@ void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_ +@@ -7100,6 +7107,7 @@ void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_ ctx->atoms.targets = XInternAtom(ctx->dpy, "TARGETS", false); ctx->atoms.gamescopeFrameHalveAtom = XInternAtom( ctx->dpy, "GAMESCOPE_STEAMUI_HALFHZ", false );; @@ -1138,7 +1129,7 @@ index 6c8ce74..dfee904 100644 ctx->root_width = DisplayWidth(ctx->dpy, ctx->scr); ctx->root_height = DisplayHeight(ctx->dpy, ctx->scr); -@@ -8061,9 +8069,10 @@ steamcompmgr_main(int argc, char **argv) +@@ -8067,9 +8075,10 @@ steamcompmgr_main(int argc, char **argv) bShouldPaint = false; } @@ -1164,16 +1155,16 @@ index e4eec9f..2347cbb 100644 bool HasQueuedEvents(); -- -2.47.0 +2.47.1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Joshua Tam <297250+joshuatam@users.noreply.github.com> Date: Fri, 6 Dec 2024 16:51:02 +0800 -Subject: feature: add rotation shader for rotating output +Subject: feat: add rotation shader for rotating output --- - src/Backends/DRMBackend.cpp | 27 +++++++- + src/Backends/DRMBackend.cpp | 29 +++++++- src/main.cpp | 6 ++ src/main.hpp | 1 + src/meson.build | 1 + @@ -1181,11 +1172,11 @@ Subject: feature: add rotation shader for rotating output src/rendervulkan.hpp | 6 +- src/shaders/cs_rotation.comp | 53 +++++++++++++++ src/wlserver.cpp | 5 ++ - 8 files changed, 206 insertions(+), 19 deletions(-) + 8 files changed, 208 insertions(+), 19 deletions(-) create mode 100644 src/shaders/cs_rotation.comp diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp -index 6bb0b88..4af3f08 100644 +index 6bb0b88..506963d 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -1752,7 +1752,7 @@ LiftoffStateCacheEntry FrameInfoToLiftoffStateCacheEntry( struct drm_t *drm, con @@ -1215,21 +1206,23 @@ index 6bb0b88..4af3f08 100644 if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO ) { m_ChosenOrientation = g_DesiredInternalOrientation; -@@ -3035,6 +3046,13 @@ bool drm_set_mode( struct drm_t *drm, const drmModeModeInfo *mode ) +@@ -3035,6 +3046,15 @@ bool drm_set_mode( struct drm_t *drm, const drmModeModeInfo *mode ) g_bRotated = false; g_nOutputWidth = mode->hdisplay; g_nOutputHeight = mode->vdisplay; + -+ if (g_bUseRotationShader) { ++ if (g_bUseRotationShader && drm->pConnector->GetScreenType() == gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL) { + g_bRotated = true; + g_nOutputWidth = mode->vdisplay; + g_nOutputHeight = mode->hdisplay; ++ } else { ++ g_bUseRotationShader = false; + } + break; case GAMESCOPE_PANEL_ORIENTATION_90: case GAMESCOPE_PANEL_ORIENTATION_270: -@@ -3294,6 +3312,11 @@ namespace gamescope +@@ -3294,6 +3314,11 @@ namespace gamescope bNeedsFullComposite |= !!(g_uCompositeDebug & CompositeDebugFlag::Heatmap); @@ -1241,7 +1234,7 @@ index 6bb0b88..4af3f08 100644 bool bDoComposite = true; if ( !bNeedsFullComposite && !bWantsPartialComposite ) { -@@ -3384,7 +3407,7 @@ namespace gamescope +@@ -3384,7 +3409,7 @@ namespace gamescope if ( bDefer && !!( g_uCompositeDebug & CompositeDebugFlag::Markers ) ) g_uCompositeDebug |= CompositeDebugFlag::Markers_Partial; @@ -1551,7 +1544,7 @@ index 54d7608..10d6c78 100644 if ( pPipewireTexture != nullptr ) diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp -index ccabd88..51a62bc 100644 +index 00f5ece..8cffcbe 100644 --- a/src/rendervulkan.hpp +++ b/src/rendervulkan.hpp @@ -393,7 +393,7 @@ gamescope::OwningRc vulkan_create_texture_from_dmabuf( struct wl @@ -1563,7 +1556,7 @@ index ccabd88..51a62bc 100644 void vulkan_wait( uint64_t ulSeqNo, bool bReset ); gamescope::Rc vulkan_get_last_output_image( bool partial, bool defer ); gamescope::Rc vulkan_acquire_screenshot_texture(uint32_t width, uint32_t height, bool exportable, uint32_t drmFormat, EStreamColorspace colorspace = k_EStreamColorspace_Unknown); -@@ -522,6 +522,9 @@ struct VulkanOutput_t +@@ -530,6 +530,9 @@ struct VulkanOutput_t // NIS gamescope::OwningRc nisScalerImage; gamescope::OwningRc nisUsmImage; @@ -1573,7 +1566,7 @@ index ccabd88..51a62bc 100644 }; -@@ -534,6 +537,7 @@ enum ShaderType { +@@ -542,6 +545,7 @@ enum ShaderType { SHADER_TYPE_RCAS, SHADER_TYPE_NIS, SHADER_TYPE_RGB_TO_NV12, @@ -1657,5 +1650,790 @@ index 1eeaa25..5aa986a 100644 *y = ty; } -- -2.47.0 +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: brainantifreeze +Date: Thu, 19 Dec 2024 09:16:15 +0000 +Subject: fix(nvidia): allow disabling Vulkan extension for nvidia to work + +This adds a workaround for #1592 which removes the +VkPhysicalDevicePresentWaitFeaturesKHR extension in +the layer if the environment variable +GAMESCOPE_WSI_HIDE_PRESENT_WAIT_EXT is set. + +This resolves the current freezing issue on nVidia +in dx12 (without having to set +VKD3D_DISABLE_EXTENSIONS), dx11 (without having +to patch DXVK not to use the extension) and in +native vulkan games. +--- + layer/VkLayer_FROG_gamescope_wsi.cpp | 20 +++++++++++++++++++- + 1 file changed, 19 insertions(+), 1 deletion(-) + +diff --git a/layer/VkLayer_FROG_gamescope_wsi.cpp b/layer/VkLayer_FROG_gamescope_wsi.cpp +index 718a260..f33da7f 100644 +--- a/layer/VkLayer_FROG_gamescope_wsi.cpp ++++ b/layer/VkLayer_FROG_gamescope_wsi.cpp +@@ -183,6 +183,16 @@ namespace GamescopeWSILayer { + return s_ensureMinImageCount; + } + ++ static bool getHidePresentWait() { ++ static bool s_hidePresentWait = []() -> bool { ++ if (auto hide = parseEnv("GAMESCOPE_WSI_HIDE_PRESENT_WAIT_EXT")) { ++ return *hide; ++ } ++ return false; ++ }(); ++ return s_hidePresentWait; ++ } ++ + // Taken from Mesa, licensed under MIT. + // + // No real reason to rewrite this code, +@@ -588,7 +598,11 @@ namespace GamescopeWSILayer { + createInfo.ppEnabledExtensionNames = enabledExts.data(); + + setenv("vk_xwayland_wait_ready", "false", 0); +- setenv("vk_khr_present_wait", "true", 0); ++ if (getHidePresentWait()) { ++ setenv("vk_khr_present_wait", "false", 0); ++ } else { ++ setenv("vk_khr_present_wait", "true", 0); ++ } + + VkResult result = pfnCreateInstanceProc(&createInfo, pAllocator, pInstance); + if (result != VK_SUCCESS) +@@ -893,6 +907,10 @@ namespace GamescopeWSILayer { + const vkroots::VkInstanceDispatch* pDispatch, + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures2* pFeatures) { ++ if (getHidePresentWait()) { ++ fprintf(stderr, "[Gamescope WSI] Removing VkPhysicalDevicePresentWaitFeaturesKHR because GAMESCOPE_WSI_HIDE_PRESENT_WAIT_EXT is set\n"); ++ vkroots::RemoveFromChain(pFeatures); ++ } + pDispatch->GetPhysicalDeviceFeatures2(physicalDevice, pFeatures); + } + +-- +2.47.1 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: "Ruan E. Formigoni" +Date: Thu, 2 Jan 2025 17:07:36 +0100 +Subject: feat: implemented bicubic downscaling + +From https://github.com/ValveSoftware/gamescope/pull/740 +--- + README.md | 1 + + src/Backends/DRMBackend.cpp | 1 + + src/Backends/OpenVRBackend.cpp | 1 + + src/Backends/SDLBackend.cpp | 4 + + src/Backends/WaylandBackend.cpp | 1 + + src/main.cpp | 54 +++++++++- + src/main.hpp | 15 +++ + src/meson.build | 1 + + src/rendervulkan.cpp | 61 ++++++++++- + src/rendervulkan.hpp | 2 + + src/shaders/bicubic.h | 44 ++++++++ + src/shaders/cs_bicubic.comp | 177 ++++++++++++++++++++++++++++++++ + src/shaders/descriptor_set.h | 1 + + src/steamcompmgr.cpp | 23 +++++ + src/steamcompmgr.hpp | 1 + + src/xwayland_ctx.hpp | 1 + + 16 files changed, 386 insertions(+), 2 deletions(-) + create mode 100644 src/shaders/bicubic.h + create mode 100644 src/shaders/cs_bicubic.comp + +diff --git a/README.md b/README.md +index 97dea45..fefb2a0 100644 +--- a/README.md ++++ b/README.md +@@ -66,6 +66,7 @@ See `gamescope --help` for a full list of options. + * `-o`: set a frame-rate limit for the game when unfocused. Specified in frames per second. Defaults to unlimited. + * `-F fsr`: use AMD FidelityFX™ Super Resolution 1.0 for upscaling + * `-F nis`: use NVIDIA Image Scaling v1.0.3 for upscaling ++* `-F bicubic`: use a bicubic filter for downscaling + * `-S integer`: use integer scaling. + * `-S stretch`: use stretch scaling, the game will fill the window. (e.g. 4:3 to 16:9) + * `-b`: create a border-less window. +diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp +index 506963d..98bdb71 100644 +--- a/src/Backends/DRMBackend.cpp ++++ b/src/Backends/DRMBackend.cpp +@@ -3293,6 +3293,7 @@ namespace gamescope + bNeedsFullComposite |= bWasFirstFrame; + bNeedsFullComposite |= pFrameInfo->useFSRLayer0; + bNeedsFullComposite |= pFrameInfo->useNISLayer0; ++ bNeedsFullComposite |= pFrameInfo->useBICUBICLayer0; + bNeedsFullComposite |= pFrameInfo->blurLayer0; + bNeedsFullComposite |= bNeedsCompositeFromFilter; + bNeedsFullComposite |= !k_bUseCursorPlane && bDrewCursor; +diff --git a/src/Backends/OpenVRBackend.cpp b/src/Backends/OpenVRBackend.cpp +index c39caa5..96a3d01 100644 +--- a/src/Backends/OpenVRBackend.cpp ++++ b/src/Backends/OpenVRBackend.cpp +@@ -554,6 +554,7 @@ namespace gamescope + bNeedsFullComposite |= cv_composite_force; + bNeedsFullComposite |= pFrameInfo->useFSRLayer0; + bNeedsFullComposite |= pFrameInfo->useNISLayer0; ++ bNeedsFullComposite |= pFrameInfo->useBICUBICLayer0; + bNeedsFullComposite |= pFrameInfo->blurLayer0; + bNeedsFullComposite |= bNeedsCompositeFromFilter; + bNeedsFullComposite |= g_bColorSliderInUse; +diff --git a/src/Backends/SDLBackend.cpp b/src/Backends/SDLBackend.cpp +index 6d50f8d..c24b864 100644 +--- a/src/Backends/SDLBackend.cpp ++++ b/src/Backends/SDLBackend.cpp +@@ -719,6 +719,10 @@ namespace gamescope + case KEY_B: + g_wantedUpscaleFilter = GamescopeUpscaleFilter::LINEAR; + break; ++ case KEY_K: ++ g_wantedDownscaleFilter = (g_wantedDownscaleFilter == GamescopeDownscaleFilter::BICUBIC) ? ++ GamescopeDownscaleFilter::LINEAR : GamescopeDownscaleFilter::BICUBIC; ++ break; + case KEY_U: + g_wantedUpscaleFilter = (g_wantedUpscaleFilter == GamescopeUpscaleFilter::FSR) ? + GamescopeUpscaleFilter::LINEAR : GamescopeUpscaleFilter::FSR; +diff --git a/src/Backends/WaylandBackend.cpp b/src/Backends/WaylandBackend.cpp +index f90c096..9f136dd 100644 +--- a/src/Backends/WaylandBackend.cpp ++++ b/src/Backends/WaylandBackend.cpp +@@ -1624,6 +1624,7 @@ namespace gamescope + bNeedsFullComposite |= cv_composite_force; + bNeedsFullComposite |= pFrameInfo->useFSRLayer0; + bNeedsFullComposite |= pFrameInfo->useNISLayer0; ++ bNeedsFullComposite |= pFrameInfo->useBICUBICLayer0; + bNeedsFullComposite |= pFrameInfo->blurLayer0; + bNeedsFullComposite |= bNeedsCompositeFromFilter; + bNeedsFullComposite |= g_bColorSliderInUse; +diff --git a/src/main.cpp b/src/main.cpp +index f61c88f..06a3bca 100644 +--- a/src/main.cpp ++++ b/src/main.cpp +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + #include + #if defined(__linux__) + #include +@@ -303,11 +304,14 @@ bool g_bGrabbed = false; + float g_mouseSensitivity = 1.0; + + GamescopeUpscaleFilter g_upscaleFilter = GamescopeUpscaleFilter::LINEAR; ++GamescopeDownscaleFilter g_downscaleFilter = GamescopeDownscaleFilter::LINEAR; + GamescopeUpscaleScaler g_upscaleScaler = GamescopeUpscaleScaler::AUTO; + + GamescopeUpscaleFilter g_wantedUpscaleFilter = GamescopeUpscaleFilter::LINEAR; ++GamescopeDownscaleFilter g_wantedDownscaleFilter = GamescopeDownscaleFilter::LINEAR; + GamescopeUpscaleScaler g_wantedUpscaleScaler = GamescopeUpscaleScaler::AUTO; + int g_upscaleFilterSharpness = 2; ++GamescopeBicubicParams g_bicubicParams; + + gamescope::GamescopeModeGeneration g_eGamescopeModeGeneration = gamescope::GAMESCOPE_MODE_GENERATE_CVT; + +@@ -427,6 +431,54 @@ static enum GamescopeUpscaleFilter parse_upscaler_filter(const char *str) + } + } + ++static enum GamescopeDownscaleFilter parse_downscaler_filter(const char *str) ++{ ++ std::string_view arg{str}; ++ ++ // If the string is just 'bicubic' use default values ++ if ( arg == "bicubic" ) { ++ return GamescopeDownscaleFilter::BICUBIC; ++ } ++ ++ // Arguments start after ':' ++ if ( auto search = arg.find(':'); search == std::string::npos ) { ++ fprintf( stderr, "gamescope: invalid argument for --filter=bicubic:float,float\n" ); ++ exit(1); ++ } else { ++ arg = std::string_view(arg.data() + search + 1); ++ } ++ ++ // Push arguments to stream ++ std::stringstream ss; ++ ss << arg; ++ ++ // Validate arguments from stream ++ double b, c; ++ char comma; ++ if ((ss >> b >> comma >> c) && (comma == ',')) { ++ // clamp values ++ b = std::clamp(b, 0.0, 1.0); ++ c = std::clamp(c, 0.0, 1.0); ++ // Ovewrite default global parameters ++ g_bicubicParams.b = b; ++ g_bicubicParams.c = c; ++ // Set downscaler filters ++ return GamescopeDownscaleFilter::BICUBIC; ++ } ++ ++ fprintf( stderr, "gamescope: invalid value for --filter\n" ); ++ exit(1); ++} ++ ++static void parse_filter(const char *str) ++{ ++ if (std::string_view{str}.starts_with("bicubic")) { ++ g_wantedDownscaleFilter = parse_downscaler_filter(str); ++ } else { ++ g_wantedUpscaleFilter = parse_upscaler_filter(str); ++ } ++} ++ + static enum gamescope::GamescopeBackend parse_backend_name(const char *str) + { + if (strcmp(str, "auto") == 0) { +@@ -756,7 +808,7 @@ int main(int argc, char **argv) + g_wantedUpscaleScaler = parse_upscaler_scaler(optarg); + break; + case 'F': +- g_wantedUpscaleFilter = parse_upscaler_filter(optarg); ++ parse_filter(optarg); + break; + case 'b': + g_bBorderlessOutputWindow = true; +diff --git a/src/main.hpp b/src/main.hpp +index 2464afa..040d04c 100644 +--- a/src/main.hpp ++++ b/src/main.hpp +@@ -43,6 +43,18 @@ enum class GamescopeUpscaleFilter : uint32_t + FROM_VIEW = 0xF, // internal + }; + ++enum class GamescopeDownscaleFilter : uint32_t ++{ ++ LINEAR = 0, ++ BICUBIC, ++}; ++ ++struct GamescopeBicubicParams ++{ ++ float b = 0.3f; ++ float c = 0.3f; ++}; ++ + static constexpr bool DoesHardwareSupportUpscaleFilter( GamescopeUpscaleFilter eFilter ) + { + // Could do nearest someday... AMDGPU DC supports custom tap placement to an extent. +@@ -60,10 +72,13 @@ enum class GamescopeUpscaleScaler : uint32_t + }; + + extern GamescopeUpscaleFilter g_upscaleFilter; ++extern GamescopeDownscaleFilter g_downscaleFilter; + extern GamescopeUpscaleScaler g_upscaleScaler; + extern GamescopeUpscaleFilter g_wantedUpscaleFilter; ++extern GamescopeDownscaleFilter g_wantedDownscaleFilter; + extern GamescopeUpscaleScaler g_wantedUpscaleScaler; + extern int g_upscaleFilterSharpness; ++extern GamescopeBicubicParams g_bicubicParams; + + extern bool g_bBorderlessOutputWindow; + +diff --git a/src/meson.build b/src/meson.build +index d4ff3ea..341bace 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -66,6 +66,7 @@ shader_src = [ + 'shaders/cs_composite_rcas.comp', + 'shaders/cs_easu.comp', + 'shaders/cs_easu_fp16.comp', ++ 'shaders/cs_bicubic.comp', + 'shaders/cs_gaussian_blur_horizontal.comp', + 'shaders/cs_nis.comp', + 'shaders/cs_nis_fp16.comp', +diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp +index 10d6c78..8b31c1e 100644 +--- a/src/rendervulkan.cpp ++++ b/src/rendervulkan.cpp +@@ -44,6 +44,7 @@ + #include "cs_composite_rcas.h" + #include "cs_easu.h" + #include "cs_easu_fp16.h" ++#include "cs_bicubic.h" + #include "cs_gaussian_blur_horizontal.h" + #include "cs_nis.h" + #include "cs_nis_fp16.h" +@@ -53,6 +54,7 @@ + #define A_CPU + #include "shaders/ffx_a.h" + #include "shaders/ffx_fsr1.h" ++#include "shaders/bicubic.h" + + #include "reshade_effect_manager.hpp" + +@@ -888,6 +890,7 @@ bool CVulkanDevice::createShaders() + SHADER(BLUR_COND, cs_composite_blur_cond); + SHADER(BLUR_FIRST_PASS, cs_gaussian_blur_horizontal); + SHADER(RCAS, cs_composite_rcas); ++ SHADER(BICUBIC, cs_bicubic); + if (m_bSupportsFp16) + { + SHADER(EASU, cs_easu_fp16); +@@ -1128,6 +1131,7 @@ void CVulkanDevice::compileAllPipelines() + SHADER(BLUR_FIRST_PASS, 1, 2, 1); + SHADER(RCAS, k_nMaxLayers, k_nMaxYcbcrMask_ToPreCompile, 1); + SHADER(EASU, 1, 1, 1); ++ SHADER(BICUBIC, 1, 1, 1); + SHADER(NIS, 1, 1, 1); + SHADER(RGB_TO_NV12, 1, 1, 1); + SHADER(ROTATION, k_nMaxLayers, k_nMaxYcbcrMask_ToPreCompile, k_nMaxBlurLayers); +@@ -3724,6 +3728,17 @@ struct EasuPushData_t + } + }; + ++struct BicubicPushData_t ++{ ++ uvec4_t Const0; ++ uvec4_t Const1; ++ ++ BicubicPushData_t(float B, float C, uint32_t inputX, uint32_t inputY, uint32_t tempX, uint32_t tempY, uint32_t winX, uint32_t winY) ++ { ++ BicubicCon(&Const0.x, &Const1.x, B*10, C*10, inputX, inputY, tempX, tempY, winX, winY); ++ } ++}; ++ + struct RcasPushData_t + { + uvec2_t u_layer0Offset; +@@ -3933,7 +3948,51 @@ std::optional vulkan_composite( struct FrameInfo_t *frameInfo, gamesco + for (uint32_t i = 0; i < EOTF_Count; i++) + cmdBuffer->bindColorMgmtLuts(i, frameInfo->shaperLut[i], frameInfo->lut3D[i]); + +- if ( frameInfo->useFSRLayer0 ) ++ if ( frameInfo->useBICUBICLayer0 ) ++ { ++ uint32_t inputX = frameInfo->layers[0].tex->width(); ++ uint32_t inputY = frameInfo->layers[0].tex->height(); ++ ++ uint32_t tempX = frameInfo->layers[0].integerWidth(); ++ uint32_t tempY = frameInfo->layers[0].integerHeight(); ++ ++ update_tmp_images(tempX, tempY); ++ ++ cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_BICUBIC, frameInfo->layerCount, frameInfo->ycbcrMask())); ++ cmdBuffer->bindTarget(g_output.tmpOutput); ++ cmdBuffer->bindTexture(0, frameInfo->layers[0].tex); ++ cmdBuffer->setTextureSrgb(0, true); ++ cmdBuffer->setSamplerUnnormalized(0, false); ++ cmdBuffer->setSamplerNearest(0, false); ++ cmdBuffer->uploadConstants(g_bicubicParams.b ++ , g_bicubicParams.c ++ , inputX ++ , inputY ++ , tempX ++ , tempY ++ , currentOutputWidth ++ , currentOutputHeight ++ ); ++ ++ int pixelsPerGroup = 16; ++ ++ cmdBuffer->dispatch(div_roundup(tempX, pixelsPerGroup), div_roundup(tempY, pixelsPerGroup)); ++ ++ struct FrameInfo_t bicFrameInfo = *frameInfo; ++ bicFrameInfo.layers[0].tex = g_output.tmpOutput; ++ bicFrameInfo.layers[0].scale.x = 1.0f; ++ bicFrameInfo.layers[0].scale.y = 1.0f; ++ ++ cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_BLIT, bicFrameInfo.layerCount, bicFrameInfo.ycbcrMask())); ++ bind_all_layers(cmdBuffer.get(), &bicFrameInfo); ++ cmdBuffer->bindTarget(compositeImage); ++ cmdBuffer->uploadConstants(&bicFrameInfo); ++ ++ pixelsPerGroup = 8; ++ ++ cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup)); ++ } ++ else if ( frameInfo->useFSRLayer0 ) + { + uint32_t inputX = frameInfo->layers[0].tex->width(); + uint32_t inputY = frameInfo->layers[0].tex->height(); +diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp +index 8cffcbe..91e2af7 100644 +--- a/src/rendervulkan.hpp ++++ b/src/rendervulkan.hpp +@@ -270,6 +270,7 @@ struct FrameInfo_t + { + bool useFSRLayer0; + bool useNISLayer0; ++ bool useBICUBICLayer0; + bool bFadingOut; + BlurMode blurLayer0; + int blurRadius; +@@ -544,6 +545,7 @@ enum ShaderType { + SHADER_TYPE_EASU, + SHADER_TYPE_RCAS, + SHADER_TYPE_NIS, ++ SHADER_TYPE_BICUBIC, + SHADER_TYPE_RGB_TO_NV12, + SHADER_TYPE_ROTATION, + +diff --git a/src/shaders/bicubic.h b/src/shaders/bicubic.h +new file mode 100644 +index 0000000..8117e87 +--- /dev/null ++++ b/src/shaders/bicubic.h +@@ -0,0 +1,44 @@ ++//_____________________________________________________________/\_______________________________________________________________ ++//============================================================================================================================== ++// ++// ++// BICUBIC IMAGE SCALING ++// ++// ++//------------------------------------------------------------------------------------------------------------------------------ ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//_____________________________________________________________/\_______________________________________________________________ ++//============================================================================================================================== ++// CONSTANT SETUP ++//============================================================================================================================== ++// Call to setup required constant values (works on CPU or GPU). ++A_STATIC void BicubicCon( ++outAU4 con0, ++outAU4 con1, ++// Configurable parameters ++AU1 B, ++AU1 C, ++// This the rendered image resolution ++AF1 inputRenderedSizeX, ++AF1 inputRenderedSizeY, ++// This is the resolution of the resource containing the input image (useful for dynamic resolution) ++AF1 inputCurrentSizeX, ++AF1 inputCurrentSizeY, ++// This is the window width / height ++AF1 outputTargetSizeX, ++AF1 outputTargetSizeY) ++{ ++ // Input/Output size information ++ con0[0]=AU1_AF1(inputRenderedSizeX); ++ con0[1]=AU1_AF1(inputRenderedSizeY); ++ con0[2]=AU1_AF1(inputCurrentSizeX); ++ con0[3]=AU1_AF1(inputCurrentSizeY); ++ ++ // Viewport pixel position to normalized image space. ++ con1[0]=AU1_AF1(outputTargetSizeX); ++ con1[1]=AU1_AF1(outputTargetSizeY); ++ con1[2]=B; ++ con1[3]=C; ++} +diff --git a/src/shaders/cs_bicubic.comp b/src/shaders/cs_bicubic.comp +new file mode 100644 +index 0000000..2b6dfb8 +--- /dev/null ++++ b/src/shaders/cs_bicubic.comp +@@ -0,0 +1,177 @@ ++// References ++// https://www.codeproject.com/Articles/236394/Bi-Cubic-and-Bi-Linear-Interpolation-with-GLSL ++// https://stackoverflow.com/questions/13501081/efficient-bicubic-filtering-code-in-glsl ++// https://web.archive.org/web/20180927181721/http://www.java-gaming.org/index.php?topic=35123.0 ++// https://gist.github.com/TheRealMJP/c83b8c0f46b63f3a88a5986f4fa982b1 ++ ++#version 460 ++ ++#extension GL_GOOGLE_include_directive : require ++#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require ++#extension GL_EXT_scalar_block_layout : require ++ ++#include "descriptor_set.h" ++ ++layout( ++ local_size_x = 64, ++ local_size_y = 1, ++ local_size_z = 1) in; ++ ++// Push constant is a mechanism in modern OpenGL that allows passing small amounts of frequently ++// updated data to the shader without needing to bind a buffer ++layout(binding = 0, scalar) ++uniform layers_t { ++ uvec4 c0, c1; ++}; ++ ++#define A_GPU 1 ++#define A_GLSL 1 ++#define A_HALF 1 ++#include "ffx_a.h" ++#include "bicubic.h" ++ ++// The Mitchell–Netravali filters or BC-splines ++// https://en.wikipedia.org/wiki/Mitchell%E2%80%93Netravali_filters ++// Conditionals are slow in GPU code, so to represent 0 <= f < 1 and 1 <= f < 2 ++// the P(d) form shown in the wikipedia page is used ++vec4 mitchellNetravaliWeights(float f, float B, float C) ++{ ++ float w0 = ((12.0 - 9.0 * B - 6.0 * C) * pow(f, 3.0)) + ++ ((-18.0 + 12.0 * B + 6.0 * C) * pow(f, 2.0)) + ++ (6.0 - 2.0 * B); ++ ++ float w1 = ((-B - 6.0 * C) * pow(f - 1.0, 3.0)) + ++ ((6.0 * B + 30.0 * C) * pow(f - 1.0, 2.0)) + ++ ((-12.0 * B - 48.0 * C) * (f - 1.0)) + ++ (8.0 * B + 24.0 * C); ++ ++ float w2 = ((12.0 - 9.0 * B - 6.0 * C) * pow(1.0 - f, 3.0)) + ++ ((-18.0 + 12.0 * B + 6.0 * C) * pow(1.0 - f, 2.0)) + ++ (6.0 - 2.0 * B); ++ ++ float w3 = ((-B - 6.0 * C) * pow(2.0 - f, 3.0)) + ++ ((6.0 * B + 30.0 * C) * pow(2.0 - f, 2.0)) + ++ ((-12.0 * B - 48.0 * C) * (2.0 - f)) + ++ (8.0 * B + 24.0 * C); ++ ++ return vec4(w0, w1, w2, w3); ++} ++ ++// https://stackoverflow.com/questions/13501081/efficient-bicubic-filtering-code-in-glsl ++// https://web.archive.org/web/20180927181721/http://www.java-gaming.org/index.php?topic=35123.0 ++// This is an efficient method to implement bicubic filtering, it takes ++// advantage of the fact that the bilinear approach gives the weighted average ++// of a 2x2 area. ++vec4 textureBicubic(sampler2D splr, vec2 texCoords) ++{ ++ vec2 texSize = textureSize(splr, 0); ++ vec2 invTexSize = 1.0 / texSize; ++ ++ // Converts normalized coordinates into pixel-space coordinate ++ // Example: If texCoords is (0.5, 0.5), and the texture size is (1920, 1080), the result will be ++ // (960, 540)—the center of the texture in pixel space. ++ // Subtracting 0.5 ensures that you're sampling from the center of the texel rather than its corner ++ // Example: Assume we have a 3x3 texture and texCoords = (0.5, 0.5): ++ // [0,0][1,0][2,0] ++ // [0,1][1,1][2,1] ++ // [0,2][1,2][2,2] ++ // texCoords * texSize - 0.5 maps to (1.5, 1.5), which is between (1,1) and (2,2), then ++ // subtracts 0.5 to move it to (1.0, 1.0)—the center of the texel ++ texCoords = texCoords * texSize - 0.5; ++ ++ // Get B and C that were pushed from the user input (or default values) ++ float B = c1[2] / 10.0f; ++ float C = c1[3] / 10.0f; ++ ++ // Get the fractional part of the coordinates ++ // They are used in Mitchell Netravali's strategy to calculate the interpolation weights, ++ // i.e., how much influence the neighboring vertices have on the final pixel value ++ vec2 fxy = fract(texCoords); ++ texCoords -= fxy; ++ ++ // Calculate bicubic weights ++ // These weights represent how much influence each neighboring texel in the 4x4 grid will have ++ // on the final interpolated pixel value ++ vec4 xweights = mitchellNetravaliWeights(fxy.x, B, C); ++ vec4 yweights = mitchellNetravaliWeights(fxy.y, B, C); ++ ++ // Modify the current texture coordinates to have an offset in texels for each coordinate ++ // E.g. texCoords + vec(-1.0, 0.0) is a texel to the left ++ // texCoords + vec(1.0, 0.0) is a texel to the right ++ // texCoords + vec(0.0, 1.0) is a texel downwards ++ // texCoords + vec(0.0, -1.0) is a texel upwards ++ vec4 offsetTexels = texCoords.xxyy; ++ offsetTexels += vec2 (-1.0, +1.0).xyxy; ++ // Normalize weights to range between (0,1) ++ // vec4 sumWeights = vec4(xweights.xz + xweights.yw, yweights.xz + yweights.yw); ++ // vec4 normalizedWeights = vec4 (xweights.yw, yweights.yw) / sumWeights; ++ vec4 sumWeights = vec4(xweights.x + xweights.y, xweights.z + xweights.w, yweights.x + yweights.y, yweights.z + yweights.w); ++ vec4 normalizedWeights = vec4 (xweights.y, xweights.w, yweights.y, yweights.w) / sumWeights; ++ // Use the weights to influence the sampling position inside each texel ++ // Each texel has a size from (0,1) ++ vec4 offsetSampler = offsetTexels + normalizedWeights; ++ // Go back to normalized space ++ offsetSampler *= invTexSize.xxyy; ++ // Perform the sampling ++ vec4 sample0 = texture(splr, offsetSampler.xz); ++ vec4 sample1 = texture(splr, offsetSampler.yz); ++ vec4 sample2 = texture(splr, offsetSampler.xw); ++ vec4 sample3 = texture(splr, offsetSampler.yw); ++ ++ // Now we perform linear interpolation in the selected points ++ // The mix(a, b, t) function in GLSL performs linear interpolation between a and b based on the ++ // parameter t, t is between 0 and 1 ++ // https://registry.khronos.org/OpenGL-Refpages/gl4/html/mix.xhtml ++ ++ // Here we want to normalize sx and sy to between 0 and 1 (t value) ++ float sx = sumWeights.x / (sumWeights.x + sumWeights.y); ++ float sy = sumWeights.z / (sumWeights.z + sumWeights.w); ++ ++ return mix( ++ mix(sample3, sample2, sx), mix(sample1, sample0, sx) ++ , sy); ++} ++ ++void bicPass(uvec2 pos) ++{ ++ // Retrieve pushed values ++ AF2 inputRenderedSize = AF2_AU2(c0.xy); ++ AF2 inputCurrentSize = AF2_AU2(c0.zw); ++ AF2 outputTargetSize = AF2_AU2(c1.xy); ++ ++ // ARcpF1(x) == 1.0 / x ++ // scaleFactor is the division between the rendered image and the size it should have at the end ++ // E.g.: Rendered 1920x1080, window size is 960x540, then scaleFactor is 2x2 ++ AF2 scaleFactor = inputRenderedSize * vec2(ARcpF1(inputCurrentSize.x), ARcpF1(inputCurrentSize.y)); ++ ++ // The parameter pos of this function is used to iterate over the output image (e.g. 960x540) ++ // The position of the processed pixel should be taken from the rendered image (e.g. 1920x1080) ++ // 10x10 in the output, corresponds to 20x20 in the original image ++ AF2 positionPixel=AF2(pos)*scaleFactor; ++ ++ // Normalize the image space to be between [0,1] ++ positionPixel=positionPixel*vec2(ARcpF1(inputRenderedSize.x),ARcpF1(inputRenderedSize.y)); ++ ++ // Apply the bicubic algorithm in the normalized pixel position ++ vec4 bicPass = textureBicubic(s_samplers[0], positionPixel); ++ ++ imageStore(dst, ivec2(pos), bicPass); ++} ++ ++ ++void main() ++{ ++ // AMD recommends to use this swizzle and to process 4 pixel per invocation ++ // for better cache utilisation ++ uvec2 pos = ARmp8x8(gl_LocalInvocationID.x) + uvec2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u); ++ ++ bicPass(pos); ++ pos.x += 8u; ++ bicPass(pos); ++ pos.y += 8u; ++ bicPass(pos); ++ pos.x -= 8u; ++ bicPass(pos); ++} ++ ++/* vim: set expandtab ft=cpp fdm=marker ts=4 sw=4 tw=100 et :*/ +diff --git a/src/shaders/descriptor_set.h b/src/shaders/descriptor_set.h +index f2b8527..64cc1c9 100644 +--- a/src/shaders/descriptor_set.h ++++ b/src/shaders/descriptor_set.h +@@ -21,6 +21,7 @@ const int filter_nearest = 1; + const int filter_fsr = 2; + const int filter_nis = 3; + const int filter_pixel = 4; ++const int filter_bicubic = 5; + const int filter_from_view = 255; + + const int EOTF_Gamma22 = 0; +diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp +index 86773db..f88576f 100644 +--- a/src/steamcompmgr.cpp ++++ b/src/steamcompmgr.cpp +@@ -906,6 +906,7 @@ gamescope::ConCommand cc_debug_set_fps_limit( "debug_set_fps_limit", "Set refres + static int g_nRuntimeInfoFd = -1; + + bool g_bFSRActive = false; ++bool g_bBicubicActive = false; + + BlurMode g_BlurMode = BLUR_MODE_OFF; + BlurMode g_BlurModeOld = BLUR_MODE_OFF; +@@ -2389,6 +2390,10 @@ paint_all(bool async, bool dpms) + paint_window(w, w, &frameInfo, global_focus.cursor, PaintWindowFlag::BasePlane | PaintWindowFlag::DrawBorders, 1.0f, override); + + bool needsScaling = frameInfo.layers[0].scale.x < 0.999f && frameInfo.layers[0].scale.y < 0.999f; ++ // Temporarily allow upscaling as well ++ // bool needsDownScaling = frameInfo.layers[0].scale.x > 1.001f && frameInfo.layers[0].scale.y > 1.001f; ++ bool needsDownScaling = true; ++ frameInfo.useBICUBICLayer0 = g_downscaleFilter == GamescopeDownscaleFilter::BICUBIC && needsDownScaling; + frameInfo.useFSRLayer0 = g_upscaleFilter == GamescopeUpscaleFilter::FSR && needsScaling; + frameInfo.useNISLayer0 = g_upscaleFilter == GamescopeUpscaleFilter::NIS && needsScaling; + } +@@ -2521,10 +2526,12 @@ paint_all(bool async, bool dpms) + } + + frameInfo.useFSRLayer0 = false; ++ frameInfo.useBICUBICLayer0 = false; + frameInfo.useNISLayer0 = false; + } + + g_bFSRActive = frameInfo.useFSRLayer0; ++ g_bBicubicActive = frameInfo.useBICUBICLayer0; + + g_bFirstFrame = false; + +@@ -5445,6 +5452,9 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) + g_wantedUpscaleScaler = GamescopeUpscaleScaler::AUTO; + g_wantedUpscaleFilter = GamescopeUpscaleFilter::NIS; + break; ++ case 5: ++ g_wantedDownscaleFilter = GamescopeDownscaleFilter::BICUBIC; ++ break; + } + hasRepaint = true; + } +@@ -7023,6 +7033,7 @@ void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_ + ctx->atoms.gamescopeLowLatency = XInternAtom( ctx->dpy, "GAMESCOPE_LOW_LATENCY", false ); + + ctx->atoms.gamescopeFSRFeedback = XInternAtom( ctx->dpy, "GAMESCOPE_FSR_FEEDBACK", false ); ++ ctx->atoms.gamescopeBicubicFeedback = XInternAtom( ctx->dpy, "GAMESCOPE_BICUBIC_FEEDBACK", false ); + + ctx->atoms.gamescopeBlurMode = XInternAtom( ctx->dpy, "GAMESCOPE_BLUR_MODE", false ); + ctx->atoms.gamescopeBlurRadius = XInternAtom( ctx->dpy, "GAMESCOPE_BLUR_RADIUS", false ); +@@ -7281,6 +7292,7 @@ extern int g_nPreferredOutputWidth; + extern int g_nPreferredOutputHeight; + + static bool g_bWasFSRActive = false; ++static bool g_bWasBicubicActive = false; + + bool g_bAppWantsHDRCached = false; + +@@ -7695,6 +7707,16 @@ steamcompmgr_main(int argc, char **argv) + flush_root = true; + } + ++ if ( g_bBicubicActive != g_bWasBicubicActive ) ++ { ++ uint32_t active = g_bBicubicActive ? 1 : 0; ++ XChangeProperty( root_ctx->dpy, root_ctx->root, root_ctx->atoms.gamescopeBicubicFeedback, XA_CARDINAL, 32, PropModeReplace, ++ (unsigned char *)&active, 1 ); ++ ++ g_bWasBicubicActive = g_bBicubicActive; ++ flush_root = true; ++ } ++ + if (global_focus.IsDirty()) + determine_and_apply_focus(); + +@@ -7931,6 +7953,7 @@ steamcompmgr_main(int argc, char **argv) + g_bSteamIsActiveWindow = false; + g_upscaleScaler = g_wantedUpscaleScaler; + g_upscaleFilter = g_wantedUpscaleFilter; ++ g_downscaleFilter = g_wantedDownscaleFilter; + } + + // If we're in the middle of a fade, then keep us +diff --git a/src/steamcompmgr.hpp b/src/steamcompmgr.hpp +index 30e48e8..5679a0c 100644 +--- a/src/steamcompmgr.hpp ++++ b/src/steamcompmgr.hpp +@@ -129,6 +129,7 @@ extern float focusedWindowOffsetY; + + extern bool g_FakeExternal; + extern bool g_bFSRActive; ++extern bool g_bBicubicActive; + + extern uint32_t inputCounter; + extern uint64_t g_lastWinSeq; +diff --git a/src/xwayland_ctx.hpp b/src/xwayland_ctx.hpp +index 2347cbb..bc38c98 100644 +--- a/src/xwayland_ctx.hpp ++++ b/src/xwayland_ctx.hpp +@@ -164,6 +164,7 @@ struct xwayland_ctx_t final : public gamescope::IWaitable + Atom gamescopeLowLatency; + + Atom gamescopeFSRFeedback; ++ Atom gamescopeBicubicFeedback; + + Atom gamescopeBlurMode; + Atom gamescopeBlurRadius; +-- +2.47.1