1013 lines
37 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 564a689c2b293d2e894bf1edb14eb0f78bde8725 Mon Sep 17 00:00:00 2001
From: "Ruan E. Formigoni" <ruanformigoni@gmail.com>
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 <mutex>
#include <vector>
#include <cstring>
+#include <sstream>
#include <string>
#if defined(__linux__)
#include <sys/capability.h>
@@ -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<uint64_t> 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<BicubicPushData_t>(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<BlitPushData_t>(&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 MitchellNetravali 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" <ruanformigoni@gmail.com>
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" <ruanformigoni@gmail.com>
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" <ruanformigoni@gmail.com>
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" <ruanformigoni@gmail.com>
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" <ruanformigoni@gmail.com>
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" <ruanformigoni@gmail.com>
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" <ruanformigoni@gmail.com>
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" <ruanformigoni@gmail.com>
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<uint64_t> 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<uint64_t> 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);