Skip to content

Commit

Permalink
feat: implement bicubic downscaling
Browse files Browse the repository at this point in the history
  • Loading branch information
ruanformigoni authored and antheas committed Jan 19, 2025
1 parent e6586e4 commit 1337c54
Show file tree
Hide file tree
Showing 16 changed files with 386 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions src/Backends/DRMBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3301,6 +3301,7 @@ namespace gamescope
bNeedsFullComposite |= bWasFirstFrame;
bNeedsFullComposite |= pFrameInfo->useFSRLayer0;
bNeedsFullComposite |= pFrameInfo->useNISLayer0;
bNeedsFullComposite |= pFrameInfo->useBICUBICLayer0;
bNeedsFullComposite |= pFrameInfo->blurLayer0;
bNeedsFullComposite |= bNeedsCompositeFromFilter;
bNeedsFullComposite |= !k_bUseCursorPlane && bDrewCursor;
Expand Down
1 change: 1 addition & 0 deletions src/Backends/OpenVRBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/Backends/SDLBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/Backends/WaylandBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1614,6 +1614,7 @@ namespace gamescope
bNeedsFullComposite |= cv_composite_force;
bNeedsFullComposite |= pFrameInfo->useFSRLayer0;
bNeedsFullComposite |= pFrameInfo->useNISLayer0;
bNeedsFullComposite |= pFrameInfo->useBICUBICLayer0;
bNeedsFullComposite |= pFrameInfo->blurLayer0;
bNeedsFullComposite |= bNeedsCompositeFromFilter;
bNeedsFullComposite |= g_bColorSliderInUse;
Expand Down
54 changes: 53 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <mutex>
#include <vector>
#include <cstring>
#include <sstream>
#include <string>
#if defined(__linux__)
#include <sys/capability.h>
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 15 additions & 0 deletions src/main.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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;

Expand Down
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
61 changes: 60 additions & 1 deletion src/rendervulkan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -3933,7 +3948,51 @@ 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 )
{
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<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();
Expand Down
2 changes: 2 additions & 0 deletions src/rendervulkan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ struct FrameInfo_t
{
bool useFSRLayer0;
bool useNISLayer0;
bool useBICUBICLayer0;
bool bFadingOut;
BlurMode blurLayer0;
int blurRadius;
Expand Down Expand Up @@ -536,6 +537,7 @@ enum ShaderType {
SHADER_TYPE_EASU,
SHADER_TYPE_RCAS,
SHADER_TYPE_NIS,
SHADER_TYPE_BICUBIC,
SHADER_TYPE_RGB_TO_NV12,
SHADER_TYPE_ROTATION,

Expand Down
44 changes: 44 additions & 0 deletions src/shaders/bicubic.h
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

0 comments on commit 1337c54

Please sign in to comment.