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);