color_helpers: added default tonemapping for PQ->G22

This commit is contained in:
Jeremy Selan 2023-05-12 16:09:25 -07:00
parent 38e5b553fd
commit 6531d9cdf9
6 changed files with 117 additions and 38 deletions

View file

@ -660,7 +660,13 @@ inline T applyShaper( const T & input, EOTF source, EOTF dest, const tonemapping
return input;
}
return calcLinearToEOTF( flGain * calcEOTFToLinear( input, source, tonemapping ), dest, tonemapping );
T flLinear = flGain * calcEOTFToLinear( input, source, tonemapping );
if ( tonemapping.bUseEEtf2390 )
{
flLinear = tonemapping.eetf2390.apply( flLinear );
}
return calcLinearToEOTF( flLinear, dest, tonemapping );
}
void calcColorTransform( lut1d_t * pShaper, int nLutSize1d,
@ -751,6 +757,12 @@ void calcColorTransform( lut1d_t * pShaper, int nLutSize1d,
// Apply night mode
destColorLinear = vNightModeMultLinear * destColorLinear * flGain;
// Apply tonemapping
if ( tonemapping.bUseEEtf2390 )
{
destColorLinear = tonemapping.eetf2390.apply( destColorLinear );
}
// Apply dest EOTF
glm::vec3 destColorEOTFEncoded = calcLinearToEOTF( destColorLinear, destEOTF, tonemapping );

View file

@ -87,17 +87,26 @@ inline T nits_to_pq( const T& nits )
struct eetf_2390_t
{
void init_nits( float sourceBlackNits, float sourceWhiteNits, float targetBlackNits, float targetWhiteNits )
enum RGBMode
{
RGBMode_Max,
RGBMode_Luma,
RGBMode_Independent,
};
void init_nits( float sourceBlackNits, float sourceWhiteNits, float targetBlackNits, float targetWhiteNits, RGBMode eMode )
{
init_pq(
nits_to_pq( sourceBlackNits ),
nits_to_pq( sourceWhiteNits ),
nits_to_pq( targetBlackNits ),
nits_to_pq( targetWhiteNits ) );
nits_to_pq( targetWhiteNits ),
eMode );
}
void init_pq( float sourceBlackPQ, float sourceWhitePQ, float targetBlackPQ, float targetWhitePQ )
void init_pq( float sourceBlackPQ, float sourceWhitePQ, float targetBlackPQ, float targetWhitePQ, RGBMode eMode )
{
m_eMode = eMode;
m_sourceBlackPQ = sourceBlackPQ;
m_sourcePQScale = sourceWhitePQ - sourceBlackPQ;
m_invSourcePQScale = m_sourcePQScale > 0.f ? 1.f / m_sourcePQScale : 0.f;
@ -106,41 +115,26 @@ struct eetf_2390_t
m_ks = 1.5 * m_maxLumPQ - 0.5; // TODO : return false if ks == 1.f?
}
// "Max RGB" approach, as defined in "Color Volume and Hue-Preservation in HDR Tone Mapping"
// Digital Object Identifier 10.5594/JMI.2020.2984046
// Date of publication: 4 May 2020
inline glm::vec3 apply_max_rgb( const glm::vec3 & inputNits )
inline glm::vec3 apply( const glm::vec3 & inputNits ) const
{
float input_scalar_nits = std::max( inputNits.r, std::max( inputNits.g, inputNits.b ) );
float output_scalar_nits = pq_to_nits( apply_pq( nits_to_pq( input_scalar_nits ) ) );
float gain = input_scalar_nits > 0.f ? output_scalar_nits / input_scalar_nits : 0.f;
return inputNits * gain;
switch ( m_eMode )
{
case RGBMode_Luma:
return apply_luma_rgb( inputNits );
case RGBMode_Independent:
return apply_independent_rgb( inputNits );
default:
return apply_max_rgb( inputNits );
}
}
inline glm::vec3 apply_luma_rgb( const glm::vec3 & inputNits )
inline float apply( float inputNits ) const
{
float input_scalar_nits = 0.2627f * inputNits.r + 0.6780f * inputNits.g + 0.0593f * inputNits.b;
float output_scalar_nits = pq_to_nits( apply_pq( nits_to_pq( input_scalar_nits ) ) );
float gain = input_scalar_nits > 0.f ? output_scalar_nits / input_scalar_nits : 0.f;
return inputNits * gain;
return pq_to_nits( apply_pq( nits_to_pq( inputNits ) ) );
}
inline glm::vec3 apply_independent_rgb( const glm::vec3 & inputNits )
{
glm::vec3 inputPQ = nits_to_pq( inputNits );
glm::vec3 outputPQ = { apply_pq( inputPQ.r ), apply_pq( inputPQ.g ), apply_pq( inputPQ.b ) };
return pq_to_nits( outputPQ );
}
float m_sourceBlackPQ = 0.f;
float m_sourcePQScale = 0.f;
float m_invSourcePQScale = 0.f;
float m_minLumPQ = 0.f;
float m_maxLumPQ = 0.f;
float m_ks = 0.f;
// Raw PQ transfer function
inline float apply_pq( float valuePQ )
inline float apply_pq( float valuePQ ) const
{
// normalize PQ based on the mastering (source) display (E1)
float e1 = ( valuePQ - m_sourceBlackPQ ) * m_invSourcePQScale;
@ -158,7 +152,41 @@ struct eetf_2390_t
}
private:
inline float _eetf_2390_spline( float value, float ks, float maxLum )
RGBMode m_eMode = RGBMode_Max;
float m_sourceBlackPQ = 0.f;
float m_sourcePQScale = 0.f;
float m_invSourcePQScale = 0.f;
float m_minLumPQ = 0.f;
float m_maxLumPQ = 0.f;
float m_ks = 0.f;
// "Max RGB" approach, as defined in "Color Volume and Hue-Preservation in HDR Tone Mapping"
// Digital Object Identifier 10.5594/JMI.2020.2984046
// Date of publication: 4 May 2020
inline glm::vec3 apply_max_rgb( const glm::vec3 & inputNits ) const
{
float input_scalar_nits = std::max( inputNits.r, std::max( inputNits.g, inputNits.b ) );
float output_scalar_nits = pq_to_nits( apply_pq( nits_to_pq( input_scalar_nits ) ) );
float gain = input_scalar_nits > 0.f ? output_scalar_nits / input_scalar_nits : 0.f;
return inputNits * gain;
}
inline glm::vec3 apply_luma_rgb( const glm::vec3 & inputNits ) const
{
float input_scalar_nits = 0.2627f * inputNits.r + 0.6780f * inputNits.g + 0.0593f * inputNits.b;
float output_scalar_nits = pq_to_nits( apply_pq( nits_to_pq( input_scalar_nits ) ) );
float gain = input_scalar_nits > 0.f ? output_scalar_nits / input_scalar_nits : 0.f;
return inputNits * gain;
}
inline glm::vec3 apply_independent_rgb( const glm::vec3 & inputNits ) const
{
glm::vec3 inputPQ = nits_to_pq( inputNits );
glm::vec3 outputPQ = { apply_pq( inputPQ.r ), apply_pq( inputPQ.g ), apply_pq( inputPQ.b ) };
return pq_to_nits( outputPQ );
}
inline float _eetf_2390_spline( float value, float ks, float maxLum ) const
{
float t = ( value - ks ) / ( 1.f - ks ); // TODO : guard against ks == 1.f?
float t_sq = t*t;
@ -245,6 +273,8 @@ struct tonemapping_t
{
bool bUseShaper = true;
float g22_luminance = 1.f; // what luminance should be applied for g22 EOTF conversions?
bool bUseEEtf2390 = false;
eetf_2390_t eetf2390;
};
struct lut1d_t

View file

@ -209,7 +209,7 @@ void test_eetf2390_mono()
printf("\n");
eetf_2390_t eetf;
eetf.init_pq( sourceBlackPQ, sourceWhitePQ, destBlackPQ, destWhitePQ );
eetf.init_pq( sourceBlackPQ, sourceWhitePQ, destBlackPQ, destWhitePQ, eetf_2390_t::RGBMode_Max );
for ( size_t nLevel=0; nLevel < 12; ++nLevel )
{

View file

@ -375,6 +375,8 @@ struct gamescope_color_mgmt_t
displaycolorimetry_t outputEncodingColorimetry;
EOTF outputEncodingEOTF;
std::shared_ptr<wlserver_hdr_metadata> appHDRMetadata = nullptr;
bool operator == (const gamescope_color_mgmt_t&) const = default;
bool operator != (const gamescope_color_mgmt_t&) const = default;
};

View file

@ -121,6 +121,7 @@ extern float g_flHDRItmSdrNits;
extern float g_flHDRItmTargetNits;
extern std::atomic<uint64_t> g_lastVblank;
static uint32_t g_uTonemapDebug = 5;
uint64_t timespec_to_nanos(struct timespec& spec)
{
@ -218,6 +219,33 @@ update_color_mgmt()
{
// PQ -> G22 Leverage the display's native brightness
tonemapping.g22_luminance = g_ColorMgmt.pending.flInternalDisplayBrightness;
if ( g_uTonemapDebug == 1 )
{
tonemapping.bUseEEtf2390 = true;
tonemapping.eetf2390.init_nits( 0.001f, 5000.f, 0.01f, 1000.f, eetf_2390_t::RGBMode_Max );
}
else if ( g_uTonemapDebug == 2 )
{
tonemapping.bUseEEtf2390 = true;
tonemapping.eetf2390.init_nits( 0.001f, 2000.f, 0.01f, 1000.f, eetf_2390_t::RGBMode_Luma );
}
else if ( g_uTonemapDebug == 3 )
{
tonemapping.bUseEEtf2390 = true;
tonemapping.eetf2390.init_nits( 0.001f, 5000.f, 0.1f, 1000.f, eetf_2390_t::RGBMode_Luma );
}
else if ( g_uTonemapDebug == 4 )
{
tonemapping.bUseEEtf2390 = true;
tonemapping.eetf2390.init_nits( 0.0f, 5000.f, 0.1f, 1000.f, eetf_2390_t::RGBMode_Luma );
}
else if ( g_uTonemapDebug == 5 )
{
tonemapping.bUseEEtf2390 = true;
tonemapping.eetf2390.init_nits( 0.001f, 5000.f, 0.5f, 1000.f, eetf_2390_t::RGBMode_Luma );
}
// xwm_log.infof("PQ -> 2.2 - tonemapping.g22_luminance %f", tonemapping.g22_luminance );
}
else if ( g_ColorMgmt.pending.outputEncodingEOTF == EOTF_PQ )
@ -5131,6 +5159,12 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev)
if ( set_color_nightmode( nightmode ) )
hasRepaint = true;
}
if ( ev->atom == ctx->atoms.gamescopeTonemapDebug )
{
g_uTonemapDebug = get_prop( ctx, ctx->root, ctx->atoms.gamescopeTonemapDebug, 0 );
g_ColorMgmt.pending.externalDirtyCtr++;
hasRepaint = true;
}
if ( ev->atom == ctx->atoms.gamescopeColorManagementDisable )
{
uint32_t val = get_prop(ctx, ctx->root, ctx->atoms.gamescopeColorManagementDisable, 0);
@ -6325,6 +6359,7 @@ void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_
ctx->atoms.gamescopeHDRItmTargetNits = XInternAtom( ctx->dpy, "GAMESCOPE_HDR_ITM_TARGET_NITS", false );
ctx->atoms.gamescopeColorLookPQ = XInternAtom( ctx->dpy, "GAMESCOPE_COLOR_LOOK_PQ", false );
ctx->atoms.gamescopeColorLookG22 = XInternAtom( ctx->dpy, "GAMESCOPE_COLOR_LOOK_G22", false );
ctx->atoms.gamescopeTonemapDebug = XInternAtom( ctx->dpy, "GAMESCOPE_TONEMAP_DEBUG", false );
ctx->atoms.gamescopeForceWindowsFullscreen = XInternAtom( ctx->dpy, "GAMESCOPE_FORCE_WINDOWS_FULLSCREEN", false );
@ -6964,7 +6999,6 @@ steamcompmgr_main(int argc, char **argv)
bool app_wants_hdr = ColorspaceIsHDR( current_app_colorspace );
static bool s_bAppWantsHDRCached = false;
static std::shared_ptr<wlserver_hdr_metadata> s_AppHDRMetadata = nullptr;
if ( app_wants_hdr != s_bAppWantsHDRCached )
{
@ -6977,7 +7011,7 @@ steamcompmgr_main(int argc, char **argv)
flush_root = true;
}
if ( app_hdr_metadata != s_AppHDRMetadata )
if ( app_hdr_metadata != g_ColorMgmt.pending.appHDRMetadata )
{
if ( app_hdr_metadata )
{
@ -6994,7 +7028,7 @@ steamcompmgr_main(int argc, char **argv)
XDeleteProperty(root_ctx->dpy, root_ctx->root, root_ctx->atoms.gamescopeColorAppHDRMetadataFeedback);
}
s_AppHDRMetadata = app_hdr_metadata;
g_ColorMgmt.pending.appHDRMetadata = app_hdr_metadata;
flush_root = true;
}
}

View file

@ -186,6 +186,7 @@ struct xwayland_ctx_t
Atom gamescopeHDRItmTargetNits;
Atom gamescopeColorLookPQ;
Atom gamescopeColorLookG22;
Atom gamescopeTonemapDebug;
Atom gamescopeForceWindowsFullscreen;