mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-07-21 06:42:02 +00:00
Decode PQ/BT.2020 desktop pixels into an FP16 linear scRGB target before compositing the color cursor, ImGui, damage overlay, and letterboxing. Encode the damaged result to PQ once at the surface boundary. Keep logical AND/XOR cursor masks in the encoded domain. This preserves premultiplied cursor edges, prevents encoded-domain blending, uses the Wayland output reference white for local overlays, and keeps guest cursor white and display calibration independent from frame SDR white. It also corrects HDR-to-SDR absolute luminance scaling, gamut matrices, black handling, and buffer-age repair for the extra pass. Suppress overlays when their conversion framebuffer is unavailable rather than allowing unconverted sRGB draws into an HDR target. Use named transfer constants for cursor shader state. Convert clipped surface rectangles explicitly before using the frame-damage merge helper, which avoids mixing its unsigned type with the EGL surface rectangle type. Only advertise native PQ when the FP16 composition target is available. SDR and native scRGB retain their direct single-pass paths.
122 lines
4.0 KiB
C
122 lines
4.0 KiB
C
/*
|
|
_______ _____ __ __ ____ __
|
|
/ ____(_)___ ___ ____ ___ ____ _ / ___// /_ ____ _____/ /__ _____ / __ \____ ______/ /__
|
|
/ / / / __ \/ _ \/ __ `__ \/ __ `/ \__ \/ __ \/ __ `/ __ / _ \/ ___/ / /_/ / __ `/ ___/ //_/
|
|
/ /___/ / / / / __/ / / / / / /_/ / ___/ / / / / /_/ / /_/ / __/ / / ____/ /_/ / /__/ ,<
|
|
\____/_/_/ /_/\___/_/ /_/ /_/\__,_/ /____/_/ /_/\__,_/\__,_/\___/_/ /_/ \__,_/\___/_/|_|
|
|
http://en.sbence.hu/ Shader: Try to get the SDR part of HDR content
|
|
*/
|
|
|
|
/**
|
|
* Translated to GLSL, original source is:
|
|
* https://github.com/VoidXH/Cinema-Shader-Pack
|
|
*/
|
|
|
|
// Configuration ---------------------------------------------------------------
|
|
const float knee = 0.75; // Compressor knee position
|
|
const float ratio = 4.0; // Compressor ratio: 1 = disabled, <1 = expander
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// Precalculated values
|
|
const float compressor = 1.0 / ratio;
|
|
|
|
// PQ constants
|
|
const float m1 = 2610.0 / 16384.0;
|
|
const float m2 = 2523.0 / 32.0;
|
|
const float m1inv = 16384.0 / 2610.0;
|
|
const float m2inv = 32.0 / 2523.0;
|
|
const float c1 = 3424.0 / 4096.0;
|
|
const float c2 = 2413.0 / 128.0;
|
|
const float c3 = 2392.0 / 128.0;
|
|
|
|
const int TRANSFER_SRGB = 0;
|
|
const int TRANSFER_SCRGB = 1;
|
|
const int TRANSFER_PQ = 2;
|
|
|
|
vec3 compress(vec3 pixel)
|
|
{
|
|
pixel = max(pixel, 0.0);
|
|
float peak = max(pixel.r, max(pixel.g, pixel.b));
|
|
if (peak <= 0.0)
|
|
return vec3(0.0);
|
|
return pixel * (peak < knee ? peak :
|
|
knee + (peak - knee) * compressor) / peak;
|
|
}
|
|
|
|
// Returns luminance in nits
|
|
vec3 pq2lin(vec3 pq, float gain)
|
|
{
|
|
vec3 p = pow(max(pq, 0.0), vec3(m2inv));
|
|
vec3 d = max(p - c1, vec3(0.0)) / (c2 - c3 * p);
|
|
return pow(d, vec3(m1inv)) * gain;
|
|
}
|
|
|
|
vec3 lin2pq(vec3 linear)
|
|
{
|
|
// ST.2084 uses absolute luminance normalized to 10000 cd/m².
|
|
vec3 p = pow(max(linear, 0.0), vec3(m1));
|
|
return pow((c1 + c2 * p) / (1.0 + c3 * p), vec3(m2));
|
|
}
|
|
|
|
vec3 srgb2lin(vec3 c)
|
|
{
|
|
vec3 v = c / 12.92;
|
|
vec3 v2 = pow((c + vec3(0.055)) / 1.055, vec3(2.4));
|
|
vec3 threshold = vec3(0.04045);
|
|
vec3 result = mix(v, v2, greaterThanEqual(c, threshold));
|
|
return result;
|
|
}
|
|
|
|
vec3 lin2srgb(vec3 c)
|
|
{
|
|
vec3 v = c * 12.92;
|
|
vec3 v2 = pow(max(c, 0.0), vec3(1.0/2.4)) * 1.055 - 0.055;
|
|
vec3 threshold = vec3(0.0031308);
|
|
vec3 result = mix(v, v2, greaterThanEqual(c, threshold));
|
|
return result;
|
|
}
|
|
|
|
// in linear space
|
|
vec3 bt2020to709(vec3 bt2020)
|
|
{
|
|
return vec3(
|
|
bt2020.r * 1.6604910 + bt2020.g * -0.5876411 + bt2020.b * -0.0728499,
|
|
bt2020.r * -0.1245505 + bt2020.g * 1.1328999 + bt2020.b * -0.0083494,
|
|
bt2020.r * -0.0181508 + bt2020.g * -0.1005789 + bt2020.b * 1.1187297);
|
|
}
|
|
|
|
// in linear space
|
|
vec3 bt709to2020(vec3 bt709)
|
|
{
|
|
return vec3(
|
|
bt709.r * 0.6274039 + bt709.g * 0.3292830 + bt709.b * 0.0433131,
|
|
bt709.r * 0.0690973 + bt709.g * 0.9195404 + bt709.b * 0.0113623,
|
|
bt709.r * 0.0163914 + bt709.g * 0.0880133 + bt709.b * 0.8955953);
|
|
}
|
|
|
|
vec3 mapToSDR(vec3 color, float gain, float contentPeak, bool pq)
|
|
{
|
|
if (pq)
|
|
{
|
|
// HDR10: PQ-encoded BT.2020. Decode the absolute 10000-nit PQ range into
|
|
// values relative to the configured SDR display peak. MaxCLL constrains
|
|
// content luminance without changing that absolute unit conversion.
|
|
color = pq2lin(color.rgb, gain);
|
|
float luminance2020 =
|
|
dot(color, vec3(0.2627002, 0.6779981, 0.0593017));
|
|
if (contentPeak > 0.0 && luminance2020 > contentPeak)
|
|
color *= contentPeak / luminance2020;
|
|
color = bt2020to709(color);
|
|
}
|
|
else
|
|
{
|
|
// scRGB is linear BT.709 with 1.0 fixed at 80 nits. Use the same
|
|
// absolute-luminance mapping as PQ before compressing the highlights.
|
|
color *= gain;
|
|
float luminance709 = dot(color, vec3(0.2126390, 0.7151687, 0.0721923));
|
|
if (contentPeak > 0.0 && luminance709 > contentPeak)
|
|
color *= contentPeak / luminance709;
|
|
}
|
|
return lin2srgb(clamp(compress(color), 0.0, 1.0));
|
|
}
|