diff --git a/src/main.hpp b/src/main.hpp index 72e2281..bac8446 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -28,7 +28,9 @@ enum class GamescopeUpscaleFilter : uint32_t LINEAR = 0, NEAREST, FSR, - NIS + NIS, + + FROM_VIEW = 255, // internal }; enum class GamescopeUpscaleScaler : uint32_t diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp index e4f5aea..128c010 100644 --- a/src/rendervulkan.cpp +++ b/src/rendervulkan.cpp @@ -3107,11 +3107,6 @@ std::shared_ptr 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 ) @@ -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 @@ -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++; @@ -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; @@ -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 @@ -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; @@ -3311,12 +3319,7 @@ 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 = bForceNearest | !layer->linearFilter; + bool nearest = layer->isScreenSize() || layer->filter == GamescopeUpscaleFilter::NEAREST || (layer->filter == GamescopeUpscaleFilter::LINEAR && !layer->viewConvertsToLinearAutomatically()); cmdBuffer->bindTexture(i, layer->tex); cmdBuffer->setTextureSrgb(i, false); cmdBuffer->setSamplerNearest(i, nearest); diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp index 0db56b4..b280963 100644 --- a/src/rendervulkan.hpp +++ b/src/rendervulkan.hpp @@ -10,6 +10,8 @@ #include #include +#include "main.hpp" + #include "shaders/descriptor_set_constants.h" class CVulkanCmdBuffer; @@ -242,6 +244,11 @@ struct vec2_t float x, y; }; +static inline bool float_is_integer(float x) +{ + return fabsf(ceilf(x) - x) <= 0.0001f; +} + struct FrameInfo_t { bool useFSRLayer0; @@ -266,8 +273,9 @@ struct FrameInfo_t float opacity; + GamescopeUpscaleFilter filter = GamescopeUpscaleFilter::LINEAR; + bool blackBorder; - bool linearFilter; bool applyColorMgmt; // drm only bool allowBlending; // drm only @@ -281,6 +289,18 @@ struct FrameInfo_t return tex->isYcbcr(); } + bool isScreenSize() const { + return scale.x >= 0.99f && scale.x <= 1.01f && + scale.y >= 0.99f && scale.y <= 1.01f && + 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 diff --git a/src/shaders/blit_push_data.h b/src/shaders/blit_push_data.h index d923223..5b845a7 100644 --- a/src/shaders/blit_push_data.h +++ b/src/shaders/blit_push_data.h @@ -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 diff --git a/src/shaders/composite.h b/src/shaders/composite.h index 03e5ba3..6238a89 100644 --- a/src/shaders/composite.h +++ b/src/shaders/composite.h @@ -71,6 +71,41 @@ 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 sampleRegular(sampler2D tex, vec2 coord, uint colorspace, bool unnormalized) { + vec4 color = textureLod(tex, coord, 0); + color.rgb = colorspace_plane_degamma_tf(color.rgb, colorspace); + return color; +} vec4 sampleLayerEx(sampler2D layerSampler, uint offsetLayerIdx, uint colorspaceLayerIdx, vec2 uv, bool unnormalized) { vec2 coord = ((uv + u_offset[offsetLayerIdx]) * u_scale[offsetLayerIdx]); @@ -89,13 +124,12 @@ 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_linear_emulated) + color = sampleBilinear(layerSampler, coord, colorspace, unnormalized); + else + color = sampleRegular(layerSampler, coord, colorspace, unnormalized); color.rgb = apply_layer_color_mgmt(color.rgb, colorspace); return color; diff --git a/src/shaders/cs_composite_blit.comp b/src/shaders/cs_composite_blit.comp index 03830c4..6048940 100644 --- a/src/shaders/cs_composite_blit.comp +++ b/src/shaders/cs_composite_blit.comp @@ -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" diff --git a/src/shaders/cs_composite_blur.comp b/src/shaders/cs_composite_blur.comp index b759b61..cccc28b 100644 --- a/src/shaders/cs_composite_blur.comp +++ b/src/shaders/cs_composite_blur.comp @@ -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" diff --git a/src/shaders/cs_composite_blur_cond.comp b/src/shaders/cs_composite_blur_cond.comp index 9918a12..0752311 100644 --- a/src/shaders/cs_composite_blur_cond.comp +++ b/src/shaders/cs_composite_blur_cond.comp @@ -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" diff --git a/src/shaders/cs_composite_rcas.comp b/src/shaders/cs_composite_rcas.comp index 8950996..8eb7178 100644 --- a/src/shaders/cs_composite_rcas.comp +++ b/src/shaders/cs_composite_rcas.comp @@ -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" @@ -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; diff --git a/src/shaders/cs_easu.comp b/src/shaders/cs_easu.comp index 0eff406..123f0c8 100644 --- a/src/shaders/cs_easu.comp +++ b/src/shaders/cs_easu.comp @@ -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" diff --git a/src/shaders/cs_easu_fp16.comp b/src/shaders/cs_easu_fp16.comp index 198dce0..a3b7a8e 100644 --- a/src/shaders/cs_easu_fp16.comp +++ b/src/shaders/cs_easu_fp16.comp @@ -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" diff --git a/src/shaders/cs_gaussian_blur_horizontal.comp b/src/shaders/cs_gaussian_blur_horizontal.comp index 8268b92..351fd76 100644 --- a/src/shaders/cs_gaussian_blur_horizontal.comp +++ b/src/shaders/cs_gaussian_blur_horizontal.comp @@ -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" @@ -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; diff --git a/src/shaders/cs_nis.comp b/src/shaders/cs_nis.comp index 5081432..d6a565e 100644 --- a/src/shaders/cs_nis.comp +++ b/src/shaders/cs_nis.comp @@ -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 diff --git a/src/shaders/cs_nis_fp16.comp b/src/shaders/cs_nis_fp16.comp index 35110aa..75a5a41 100644 --- a/src/shaders/cs_nis_fp16.comp +++ b/src/shaders/cs_nis_fp16.comp @@ -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 diff --git a/src/shaders/cs_rgb_to_nv12.comp b/src/shaders/cs_rgb_to_nv12.comp index fbf1b90..6e408ee 100644 --- a/src/shaders/cs_rgb_to_nv12.comp +++ b/src/shaders/cs_rgb_to_nv12.comp @@ -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" @@ -15,6 +16,7 @@ layout( // UVUVUVUVUVUVUVU... const uint u_frameId = 0; +const uint8_t u_shaderFilter[VKR_MAX_LAYERS] = { uint8_t(0), uint8_t(0), uint8_t(0), uint8_t(0), uint8_t(0), uint8_t(0) }; const float u_linearToNits = 400.0f; const float u_nitsToLinear = 1.0f / 100.0f; const float u_itmSdrNits = 100.f; diff --git a/src/shaders/descriptor_set.h b/src/shaders/descriptor_set.h index 87d7919..9f76b1d 100644 --- a/src/shaders/descriptor_set.h +++ b/src/shaders/descriptor_set.h @@ -16,6 +16,12 @@ const int colorspace_pq = 3; const int colorspace_reserved = 3; const int colorspace_max_bits = 2; +const int filter_linear_emulated = 0; +const int filter_nearest = 1; +const int filter_fsr = 2; +const int filter_nis = 3; +const int filter_from_view = 255; + const int EOTF_Gamma22 = 0; const int EOTF_PQ = 1; const int EOTF_Count = 2; diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 81e0553..f276e5e 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -1943,7 +1943,7 @@ void MouseCursor::paint(steamcompmgr_win_t *window, steamcompmgr_win_t *fit, str layer->tex = m_texture; layer->fbid = BIsNested() ? 0 : m_texture->fbid(); - layer->linearFilter = cursor_scale != 1.0f; + layer->filter = cursor_scale != 1.0f ? GamescopeUpscaleFilter::LINEAR : GamescopeUpscaleFilter::NEAREST; layer->blackBorder = false; layer->colorspace = GAMESCOPE_APP_TEXTURE_COLORSPACE_SRGB; } @@ -1973,6 +1973,7 @@ struct BaseLayerInfo_t float scale[2]; float offset[2]; float opacity; + GamescopeUpscaleFilter filter; }; std::array< BaseLayerInfo_t, HELD_COMMIT_COUNT > g_CachedPlanes = {}; @@ -1994,7 +1995,7 @@ paint_cached_base_layer(const std::shared_ptr& commit, const BaseLayer layer->tex = commit->vulkanTex; layer->fbid = commit->fb_id; - layer->linearFilter = true; + layer->filter = base.filter; layer->blackBorder = true; } @@ -2194,7 +2195,7 @@ paint_window(steamcompmgr_win_t *w, steamcompmgr_win_t *scaleW, struct FrameInfo layer->tex = lastCommit->vulkanTex; layer->fbid = lastCommit->fb_id; - layer->linearFilter = (w->isOverlay || w->isExternalOverlay) ? true : g_upscaleFilter != GamescopeUpscaleFilter::NEAREST; + layer->filter = (w->isOverlay || w->isExternalOverlay) ? GamescopeUpscaleFilter::LINEAR : g_upscaleFilter; layer->colorspace = lastCommit->colorspace(); if ( flags & PaintWindowFlag::BasePlane ) @@ -2205,6 +2206,7 @@ paint_window(steamcompmgr_win_t *w, steamcompmgr_win_t *scaleW, struct FrameInfo basePlane.offset[0] = layer->offset.x; basePlane.offset[1] = layer->offset.y; basePlane.opacity = layer->opacity; + basePlane.filter = layer->filter; g_CachedPlanes[ HELD_COMMIT_BASE ] = basePlane; if ( !(flags & PaintWindowFlag::FadeTarget) ) @@ -2666,7 +2668,7 @@ paint_all(bool async) baseLayer->applyColorMgmt = false; baseLayer->allowBlending = false; - baseLayer->linearFilter = false; + baseLayer->filter = GamescopeUpscaleFilter::NEAREST; baseLayer->colorspace = g_bOutputHDREnabled ? GAMESCOPE_APP_TEXTURE_COLORSPACE_HDR10_PQ : GAMESCOPE_APP_TEXTURE_COLORSPACE_SRGB; g_bWasPartialComposite = false; @@ -2692,7 +2694,7 @@ paint_all(bool async) overlayLayer->applyColorMgmt = g_ColorMgmt.pending.enabled; overlayLayer->allowBlending = g_ColorMgmt.pending.enabled; - overlayLayer->linearFilter = false; + overlayLayer->filter = GamescopeUpscaleFilter::NEAREST; // Partial composition stuff has the same colorspace. // So read that from the composite frame info overlayLayer->colorspace = compositeFrameInfo.layers[0].colorspace;