color_helpers: added default tonemapping for PQ->G22
This commit is contained in:
parent
38e5b553fd
commit
6531d9cdf9
6 changed files with 117 additions and 38 deletions
|
@ -660,7 +660,13 @@ inline T applyShaper( const T & input, EOTF source, EOTF dest, const tonemapping
|
||||||
return input;
|
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,
|
void calcColorTransform( lut1d_t * pShaper, int nLutSize1d,
|
||||||
|
@ -751,6 +757,12 @@ void calcColorTransform( lut1d_t * pShaper, int nLutSize1d,
|
||||||
// Apply night mode
|
// Apply night mode
|
||||||
destColorLinear = vNightModeMultLinear * destColorLinear * flGain;
|
destColorLinear = vNightModeMultLinear * destColorLinear * flGain;
|
||||||
|
|
||||||
|
// Apply tonemapping
|
||||||
|
if ( tonemapping.bUseEEtf2390 )
|
||||||
|
{
|
||||||
|
destColorLinear = tonemapping.eetf2390.apply( destColorLinear );
|
||||||
|
}
|
||||||
|
|
||||||
// Apply dest EOTF
|
// Apply dest EOTF
|
||||||
glm::vec3 destColorEOTFEncoded = calcLinearToEOTF( destColorLinear, destEOTF, tonemapping );
|
glm::vec3 destColorEOTFEncoded = calcLinearToEOTF( destColorLinear, destEOTF, tonemapping );
|
||||||
|
|
||||||
|
|
|
@ -87,17 +87,26 @@ inline T nits_to_pq( const T& nits )
|
||||||
|
|
||||||
struct eetf_2390_t
|
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(
|
init_pq(
|
||||||
nits_to_pq( sourceBlackNits ),
|
nits_to_pq( sourceBlackNits ),
|
||||||
nits_to_pq( sourceWhiteNits ),
|
nits_to_pq( sourceWhiteNits ),
|
||||||
nits_to_pq( targetBlackNits ),
|
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_sourceBlackPQ = sourceBlackPQ;
|
||||||
m_sourcePQScale = sourceWhitePQ - sourceBlackPQ;
|
m_sourcePQScale = sourceWhitePQ - sourceBlackPQ;
|
||||||
m_invSourcePQScale = m_sourcePQScale > 0.f ? 1.f / m_sourcePQScale : 0.f;
|
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?
|
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"
|
inline glm::vec3 apply( const glm::vec3 & inputNits ) const
|
||||||
// Digital Object Identifier 10.5594/JMI.2020.2984046
|
|
||||||
// Date of publication: 4 May 2020
|
|
||||||
inline glm::vec3 apply_max_rgb( const glm::vec3 & inputNits )
|
|
||||||
{
|
{
|
||||||
float input_scalar_nits = std::max( inputNits.r, std::max( inputNits.g, inputNits.b ) );
|
switch ( m_eMode )
|
||||||
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;
|
case RGBMode_Luma:
|
||||||
return inputNits * gain;
|
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;
|
return pq_to_nits( apply_pq( nits_to_pq( inputNits ) ) );
|
||||||
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 )
|
|
||||||
{
|
|
||||||
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
|
// 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)
|
// normalize PQ based on the mastering (source) display (E1)
|
||||||
float e1 = ( valuePQ - m_sourceBlackPQ ) * m_invSourcePQScale;
|
float e1 = ( valuePQ - m_sourceBlackPQ ) * m_invSourcePQScale;
|
||||||
|
@ -158,7 +152,41 @@ struct eetf_2390_t
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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 = ( value - ks ) / ( 1.f - ks ); // TODO : guard against ks == 1.f?
|
||||||
float t_sq = t*t;
|
float t_sq = t*t;
|
||||||
|
@ -245,6 +273,8 @@ struct tonemapping_t
|
||||||
{
|
{
|
||||||
bool bUseShaper = true;
|
bool bUseShaper = true;
|
||||||
float g22_luminance = 1.f; // what luminance should be applied for g22 EOTF conversions?
|
float g22_luminance = 1.f; // what luminance should be applied for g22 EOTF conversions?
|
||||||
|
bool bUseEEtf2390 = false;
|
||||||
|
eetf_2390_t eetf2390;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lut1d_t
|
struct lut1d_t
|
||||||
|
|
|
@ -209,7 +209,7 @@ void test_eetf2390_mono()
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
eetf_2390_t eetf;
|
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 )
|
for ( size_t nLevel=0; nLevel < 12; ++nLevel )
|
||||||
{
|
{
|
||||||
|
|
|
@ -375,6 +375,8 @@ struct gamescope_color_mgmt_t
|
||||||
displaycolorimetry_t outputEncodingColorimetry;
|
displaycolorimetry_t outputEncodingColorimetry;
|
||||||
EOTF outputEncodingEOTF;
|
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;
|
||||||
bool operator != (const gamescope_color_mgmt_t&) const = default;
|
bool operator != (const gamescope_color_mgmt_t&) const = default;
|
||||||
};
|
};
|
||||||
|
|
|
@ -121,6 +121,7 @@ extern float g_flHDRItmSdrNits;
|
||||||
extern float g_flHDRItmTargetNits;
|
extern float g_flHDRItmTargetNits;
|
||||||
|
|
||||||
extern std::atomic<uint64_t> g_lastVblank;
|
extern std::atomic<uint64_t> g_lastVblank;
|
||||||
|
static uint32_t g_uTonemapDebug = 5;
|
||||||
|
|
||||||
uint64_t timespec_to_nanos(struct timespec& spec)
|
uint64_t timespec_to_nanos(struct timespec& spec)
|
||||||
{
|
{
|
||||||
|
@ -218,6 +219,33 @@ update_color_mgmt()
|
||||||
{
|
{
|
||||||
// PQ -> G22 Leverage the display's native brightness
|
// PQ -> G22 Leverage the display's native brightness
|
||||||
tonemapping.g22_luminance = g_ColorMgmt.pending.flInternalDisplayBrightness;
|
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 );
|
// xwm_log.infof("PQ -> 2.2 - tonemapping.g22_luminance %f", tonemapping.g22_luminance );
|
||||||
}
|
}
|
||||||
else if ( g_ColorMgmt.pending.outputEncodingEOTF == EOTF_PQ )
|
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 ) )
|
if ( set_color_nightmode( nightmode ) )
|
||||||
hasRepaint = true;
|
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 )
|
if ( ev->atom == ctx->atoms.gamescopeColorManagementDisable )
|
||||||
{
|
{
|
||||||
uint32_t val = get_prop(ctx, ctx->root, ctx->atoms.gamescopeColorManagementDisable, 0);
|
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.gamescopeHDRItmTargetNits = XInternAtom( ctx->dpy, "GAMESCOPE_HDR_ITM_TARGET_NITS", false );
|
||||||
ctx->atoms.gamescopeColorLookPQ = XInternAtom( ctx->dpy, "GAMESCOPE_COLOR_LOOK_PQ", 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.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 );
|
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 );
|
bool app_wants_hdr = ColorspaceIsHDR( current_app_colorspace );
|
||||||
|
|
||||||
static bool s_bAppWantsHDRCached = false;
|
static bool s_bAppWantsHDRCached = false;
|
||||||
static std::shared_ptr<wlserver_hdr_metadata> s_AppHDRMetadata = nullptr;
|
|
||||||
|
|
||||||
if ( app_wants_hdr != s_bAppWantsHDRCached )
|
if ( app_wants_hdr != s_bAppWantsHDRCached )
|
||||||
{
|
{
|
||||||
|
@ -6977,7 +7011,7 @@ steamcompmgr_main(int argc, char **argv)
|
||||||
flush_root = true;
|
flush_root = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( app_hdr_metadata != s_AppHDRMetadata )
|
if ( app_hdr_metadata != g_ColorMgmt.pending.appHDRMetadata )
|
||||||
{
|
{
|
||||||
if ( app_hdr_metadata )
|
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);
|
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;
|
flush_root = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,6 +186,7 @@ struct xwayland_ctx_t
|
||||||
Atom gamescopeHDRItmTargetNits;
|
Atom gamescopeHDRItmTargetNits;
|
||||||
Atom gamescopeColorLookPQ;
|
Atom gamescopeColorLookPQ;
|
||||||
Atom gamescopeColorLookG22;
|
Atom gamescopeColorLookG22;
|
||||||
|
Atom gamescopeTonemapDebug;
|
||||||
|
|
||||||
Atom gamescopeForceWindowsFullscreen;
|
Atom gamescopeForceWindowsFullscreen;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue