rendervulkan: Emulate bilinear sampling when we need to do custom degammas
Fixes blending of PQ/HDR10 and 10-bit sRGB surfaces
This commit is contained in:
parent
23d2f8745c
commit
e17627beef
17 changed files with 111 additions and 24 deletions
|
@ -28,7 +28,9 @@ enum class GamescopeUpscaleFilter : uint32_t
|
|||
LINEAR = 0,
|
||||
NEAREST,
|
||||
FSR,
|
||||
NIS
|
||||
NIS,
|
||||
|
||||
FROM_VIEW = 255, // internal
|
||||
};
|
||||
|
||||
enum class GamescopeUpscaleScaler : uint32_t
|
||||
|
|
|
@ -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 )
|
||||
|
@ -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);
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <array>
|
||||
#include <bitset>
|
||||
|
||||
#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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_t>& 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;
|
||||
|
|
Loading…
Reference in a new issue