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 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 );
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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,9 +7011,9 @@ 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)
|
||||
if ( app_hdr_metadata )
|
||||
{
|
||||
std::vector<uint32_t> app_hdr_metadata_blob;
|
||||
app_hdr_metadata_blob.resize((sizeof(hdr_metadata_infoframe) + (sizeof(uint32_t) - 1)) / sizeof(uint32_t));
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -186,6 +186,7 @@ struct xwayland_ctx_t
|
|||
Atom gamescopeHDRItmTargetNits;
|
||||
Atom gamescopeColorLookPQ;
|
||||
Atom gamescopeColorLookG22;
|
||||
Atom gamescopeTonemapDebug;
|
||||
|
||||
Atom gamescopeForceWindowsFullscreen;
|
||||
|
||||
|
|
Loading…
Reference in a new issue