Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rendervulkan: Add PIXEL filter (band-limited pixel filter) #974

Merged
merged 3 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions protocol/gamescope-control.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<entry name="done" value="0" summary="sent at the end of the feature list"/>
<entry name="reshade_shaders" value="1"/>
<entry name="display_info" value="2"/>
<entry name="pixel_filter" value="3"/>
</enum>

<event name="feature_support">
Expand Down
4 changes: 3 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ const char usage[] =
" -r, --nested-refresh game refresh rate (frames per second)\n"
" -m, --max-scale maximum scale factor\n"
" -S, --scaler upscaler type (auto, integer, fit, fill, stretch)\n"
" -F, --filter upscaler filter (linear, nearest, fsr, nis)\n"
" -F, --filter upscaler filter (linear, nearest, fsr, nis, pixel)\n"
" fsr => AMD FidelityFX™ Super Resolution 1.0\n"
" nis => NVIDIA Image Scaling v1.0.3\n"
" --sharpness, --fsr-sharpness upscaler sharpness from 0 (max) to 20 (min)\n"
Expand Down Expand Up @@ -391,6 +391,8 @@ static enum GamescopeUpscaleFilter parse_upscaler_filter(const char *str)
return GamescopeUpscaleFilter::FSR;
} else if (strcmp(str, "nis") == 0) {
return GamescopeUpscaleFilter::NIS;
} else if (strcmp(str, "pixel") == 0) {
return GamescopeUpscaleFilter::PIXEL;
} else {
fprintf( stderr, "gamescope: invalid value for --filter\n" );
exit(1);
Expand Down
5 changes: 4 additions & 1 deletion src/main.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ enum class GamescopeUpscaleFilter : uint32_t
LINEAR = 0,
NEAREST,
FSR,
NIS
NIS,
PIXEL,

FROM_VIEW = 255, // internal
};

enum class GamescopeUpscaleScaler : uint32_t
Expand Down
26 changes: 16 additions & 10 deletions src/rendervulkan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3107,11 +3107,6 @@ std::shared_ptr<CVulkanTexture> vulkan_create_texture_from_bits( uint32_t width,
return pTex;
}

bool float_is_integer(float x)
{
return fabsf(ceilf(x) - x) <= 0.0001f;
}

static uint32_t s_frameId = 0;

void vulkan_garbage_collect( void )
Expand Down Expand Up @@ -3167,6 +3162,9 @@ struct BlitPushData_t
uint32_t frameId;
uint32_t blurRadius;

uint8_t u_shaderFilter[k_nMaxLayers];
uint8_t u_padding[2];

float u_linearToNits; // unset
float u_nitsToLinear; // unset
float u_itmSdrNits; // unset
Expand All @@ -3179,6 +3177,11 @@ struct BlitPushData_t
scale[i] = layer->scale;
offset[i] = layer->offsetPixelCenter();
opacity[i] = layer->opacity;
if (layer->isScreenSize() || (layer->filter == GamescopeUpscaleFilter::LINEAR && layer->viewConvertsToLinearAutomatically()))
u_shaderFilter[i] = (uint32_t)GamescopeUpscaleFilter::FROM_VIEW;
else
u_shaderFilter[i] = (uint32_t)layer->filter;

}
borderMask = frameInfo->borderMask();
frameId = s_frameId++;
Expand All @@ -3194,6 +3197,7 @@ struct BlitPushData_t
scale[0] = { blit_scale, blit_scale };
offset[0] = { 0.5f, 0.5f };
opacity[0] = 1.0f;
u_shaderFilter[0] = (uint32_t)GamescopeUpscaleFilter::LINEAR;
borderMask = 0;
frameId = s_frameId;

Expand Down Expand Up @@ -3258,6 +3262,9 @@ struct RcasPushData_t
uint32_t u_frameId;
uint32_t u_c1;

uint8_t u_shaderFilter[k_nMaxLayers];
uint8_t u_padding[2];

float u_linearToNits; // unset
float u_nitsToLinear; // unset
float u_itmSdrNits; // unset
Expand All @@ -3270,6 +3277,7 @@ struct RcasPushData_t
u_layer0Offset.x = uint32_t(int32_t(frameInfo->layers[0].offset.x));
u_layer0Offset.y = uint32_t(int32_t(frameInfo->layers[0].offset.y));
u_opacity[0] = frameInfo->layers[0].opacity;
u_shaderFilter[0] = (uint32_t)GamescopeUpscaleFilter::FROM_VIEW;
u_borderMask = frameInfo->borderMask() >> 1u;
u_frameId = s_frameId++;
u_c1 = tmp.x;
Expand Down Expand Up @@ -3311,12 +3319,10 @@ void bind_all_layers(CVulkanCmdBuffer* cmdBuffer, const struct FrameInfo_t *fram
{
const FrameInfo_t::Layer_t *layer = &frameInfo->layers[i];

bool bForceNearest = layer->scale.x == 1.0f &&
layer->scale.y == 1.0f &&
float_is_integer(layer->offset.x) &&
float_is_integer(layer->offset.y);
bool nearest = layer->isScreenSize()
|| layer->filter == GamescopeUpscaleFilter::NEAREST
|| (layer->filter == GamescopeUpscaleFilter::LINEAR && !layer->viewConvertsToLinearAutomatically());

bool nearest = bForceNearest | !layer->linearFilter;
cmdBuffer->bindTexture(i, layer->tex);
cmdBuffer->setTextureSrgb(i, false);
cmdBuffer->setSamplerNearest(i, nearest);
Expand Down
27 changes: 26 additions & 1 deletion src/rendervulkan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <array>
#include <bitset>

#include "main.hpp"

#include "shaders/descriptor_set_constants.h"

class CVulkanCmdBuffer;
Expand Down Expand Up @@ -242,6 +244,16 @@ struct vec2_t
float x, y;
};

static inline bool float_is_integer(float x)
{
return fabsf(ceilf(x) - x) <= 0.001f;
}

inline bool close_enough(float a, float b, float epsilon = 0.001f)
{
return fabsf(a - b) <= epsilon;
}

struct FrameInfo_t
{
bool useFSRLayer0;
Expand All @@ -266,8 +278,9 @@ struct FrameInfo_t

float opacity;

GamescopeUpscaleFilter filter = GamescopeUpscaleFilter::LINEAR;

bool blackBorder;
bool linearFilter;
bool applyColorMgmt; // drm only
bool allowBlending; // drm only

Expand All @@ -281,6 +294,18 @@ struct FrameInfo_t
return tex->isYcbcr();
}

bool isScreenSize() const {
return close_enough(scale.x, 1.0f) &&
close_enough(scale.y, 1.0f) &&
float_is_integer(offset.x) &&
float_is_integer(offset.y);
}

bool viewConvertsToLinearAutomatically() const {
return colorspace == GAMESCOPE_APP_TEXTURE_COLORSPACE_LINEAR ||
colorspace == GAMESCOPE_APP_TEXTURE_COLORSPACE_SCRGB;
}

uint32_t integerWidth() const { return tex->width() / scale.x; }
uint32_t integerHeight() const { return tex->height() / scale.y; }
vec2_t offsetPixelCenter() const
Expand Down
2 changes: 1 addition & 1 deletion src/sdlwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ void inputSDLThreadRun( void )
SDL_SetWindowFullscreen( g_SDLWindow, g_bFullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0 );
break;
case KEY_N:
g_wantedUpscaleFilter = GamescopeUpscaleFilter::NEAREST;
g_wantedUpscaleFilter = GamescopeUpscaleFilter::PIXEL;
break;
case KEY_B:
g_wantedUpscaleFilter = GamescopeUpscaleFilter::LINEAR;
Expand Down
3 changes: 3 additions & 0 deletions src/shaders/blit_push_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ uniform layers_t {
uint u_frameId;
uint u_blur_radius;

uint8_t u_shaderFilter[VKR_MAX_LAYERS];
uint8_t u_padding[2];

// hdr
float u_linearToNits; // sdr -> hdr
float u_nitsToLinear; // hdr -> sdr
Expand Down
87 changes: 81 additions & 6 deletions src/shaders/composite.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,45 @@
#include "colorimetry.h"

vec4 sampleRegular(sampler2D tex, vec2 coord, uint colorspace) {
vec4 color = textureLod(tex, coord, 0);
color.rgb = colorspace_plane_degamma_tf(color.rgb, colorspace);
return color;
}

// To be considered pseudo-bandlimited, upscaling factor must be at least 2x.

const float bandlimited_PI = 3.14159265359;
const float bandlimited_PI_half = 0.5 * bandlimited_PI;
// size: resolution of sampled texture
// inv_size: inverse resolution of sampled texture
// extent: Screen-space gradient of UV in texels. Typically computed as (texture resolution) / (viewport resolution).
// If screen is rotated by 90 or 270 degrees, the derivatives need to be computed appropriately.
// For uniform scaling, none of this matters.
// extent can be multiplied to achieve LOD bias.
// extent must be at least 1.0 / 256.0.
vec4 sampleBandLimited(sampler2D samp, vec2 uv, vec2 size, vec2 inv_size, vec2 extent, uint colorspace, bool unnormalized)
{
// Josh:
// Clamp to behaviour like 4x scale (0.25).
//
// Was defaulted to 2x before (0.5), which is 1px, but gives blurry result
// on Cave Story (480p) -> 800p on Deck.
// TODO: Maybe make this configurable?
const float max_extent = 0.25f;

// Get base pixel and phase, range [0, 1).
vec2 pixel = uv * (unnormalized ? vec2(1.0f) : size) - 0.5;
vec2 base_pixel = floor(pixel);
vec2 phase = pixel - base_pixel;

// We can resolve the filter by just sampling a single 2x2 block.
// Lerp between normal sampling at LOD 0, and bandlimited pixel filter at LOD -1.
vec2 shift = 0.5 + 0.5 * sin(bandlimited_PI_half * clamp((phase - 0.5) / min(extent, vec2(max_extent)), -1.0, 1.0));
uv = (base_pixel + 0.5 + shift) * (unnormalized ? vec2(1.0f) : inv_size);

return sampleRegular(samp, uv, colorspace);
}

uint pseudo_random(uint seed) {
seed ^= (seed << 13);
seed ^= (seed >> 17);
Expand Down Expand Up @@ -71,6 +111,35 @@ vec3 apply_layer_color_mgmt(vec3 color, uint colorspace) {
return color;
}

vec4 sampleBilinear(sampler2D tex, vec2 coord, uint colorspace, bool unnormalized) {
vec2 scale = unnormalized ? vec2(1.0) : vec2(textureSize(tex, 0));

vec2 pixCoord = coord * scale - 0.5f;
vec2 originPixCoord = floor(pixCoord);

vec2 gatherUV = (originPixCoord * scale + 1.0f) / scale;

vec4 red = textureGather(tex, gatherUV, 0);
vec4 green = textureGather(tex, gatherUV, 1);
vec4 blue = textureGather(tex, gatherUV, 2);
vec4 alpha = textureGather(tex, gatherUV, 3);

vec4 c00 = vec4(red.w, green.w, blue.w, alpha.w);
vec4 c01 = vec4(red.x, green.x, blue.x, alpha.x);
vec4 c11 = vec4(red.y, green.y, blue.y, alpha.y);
vec4 c10 = vec4(red.z, green.z, blue.z, alpha.z);

c00.rgb = colorspace_plane_degamma_tf(c00.rgb, colorspace);
c01.rgb = colorspace_plane_degamma_tf(c01.rgb, colorspace);
c11.rgb = colorspace_plane_degamma_tf(c11.rgb, colorspace);
c10.rgb = colorspace_plane_degamma_tf(c10.rgb, colorspace);

vec2 filterWeight = pixCoord - originPixCoord;

vec4 temp0 = mix(c01, c11, filterWeight.x);
vec4 temp1 = mix(c00, c10, filterWeight.x);
return mix(temp1, temp0, filterWeight.y);
}

vec4 sampleLayerEx(sampler2D layerSampler, uint offsetLayerIdx, uint colorspaceLayerIdx, vec2 uv, bool unnormalized) {
vec2 coord = ((uv + u_offset[offsetLayerIdx]) * u_scale[offsetLayerIdx]);
Expand All @@ -89,13 +158,19 @@ vec4 sampleLayerEx(sampler2D layerSampler, uint offsetLayerIdx, uint colorspaceL
if (!unnormalized)
coord /= texSize;

vec4 color = textureLod(layerSampler, coord, 0.0f);

// TODO(Josh): If colorspace != linear, emulate bilinear ourselves to blend
// in linear space!
// Split this into two parts!
uint colorspace = get_layer_colorspace(colorspaceLayerIdx);
color.rgb = colorspace_plane_degamma_tf(color.rgb, colorspace);
vec4 color;
if (u_shaderFilter[offsetLayerIdx] == filter_pixel) {
vec2 output_res = texSize / u_scale[offsetLayerIdx];
vec2 extent = max((texSize / output_res), vec2(1.0 / 256.0));
color = sampleBandLimited(layerSampler, coord, unnormalized ? vec2(1.0f) : texSize, unnormalized ? vec2(1.0f) : vec2(1.0f) / texSize, extent, colorspace, unnormalized);
}
else if (u_shaderFilter[offsetLayerIdx] == filter_linear_emulated) {
color = sampleBilinear(layerSampler, coord, colorspace, unnormalized);
}
else {
color = sampleRegular(layerSampler, coord, colorspace);
}
color.rgb = apply_layer_color_mgmt(color.rgb, colorspace);

return color;
Expand Down
1 change: 1 addition & 0 deletions src/shaders/cs_composite_blit.comp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#version 450

#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require

#include "descriptor_set.h"

Expand Down
1 change: 1 addition & 0 deletions src/shaders/cs_composite_blur.comp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#version 450

#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require

#include "descriptor_set.h"

Expand Down
1 change: 1 addition & 0 deletions src/shaders/cs_composite_blur_cond.comp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#version 450

#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require

#include "descriptor_set.h"

Expand Down
4 changes: 4 additions & 0 deletions src/shaders/cs_composite_rcas.comp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#version 460

#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require

#include "descriptor_set.h"

Expand All @@ -19,6 +20,9 @@ uniform layers_t {
uint u_frameId;
uint u_c1;

uint8_t u_shaderFilter[VKR_MAX_LAYERS];
uint8_t u_padding[2];

// hdr
float u_linearToNits;
float u_nitsToLinear;
Expand Down
1 change: 1 addition & 0 deletions src/shaders/cs_easu.comp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#version 460

#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require

#include "descriptor_set.h"

Expand Down
1 change: 1 addition & 0 deletions src/shaders/cs_easu_fp16.comp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require
#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require

#include "descriptor_set.h"

Expand Down
4 changes: 4 additions & 0 deletions src/shaders/cs_gaussian_blur_horizontal.comp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#version 460

#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require

#include "descriptor_set.h"

Expand All @@ -18,6 +19,9 @@ uniform layers_t {
uint u_frameId;
uint u_blur_radius;

uint8_t u_shaderFilter[VKR_MAX_LAYERS];
uint8_t u_padding[2];

// hdr
float u_linearToNits;
float u_nitsToLinear;
Expand Down
1 change: 1 addition & 0 deletions src/shaders/cs_nis.comp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#version 450

#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require

#define NIS_GLSL 1
#define NIS_SCALER 1
Expand Down
1 change: 1 addition & 0 deletions src/shaders/cs_nis_fp16.comp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require
#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require

#define NIS_GLSL 1
#define NIS_USE_HALF_PRECISION 1
Expand Down
Loading
Loading