diff --git a/src/color_helpers.cpp b/src/color_helpers.cpp index 577f0dc..17685b1 100644 --- a/src/color_helpers.cpp +++ b/src/color_helpers.cpp @@ -210,7 +210,7 @@ glm::vec3 calcEOTFToLinear( const glm::vec3 & input, EOTF eotf, const tonemappin { if ( eotf == EOTF_Gamma22 ) { - return glm::pow( input, glm::vec3( 2.2f ) ) * tonemapping.g22_luminance_tolinear; + return glm::pow( input, glm::vec3( 2.2f ) ) * tonemapping.g22_luminance; } else if ( eotf == EOTF_PQ ) { @@ -225,9 +225,9 @@ glm::vec3 calcLinearToEOTF( const glm::vec3 & input, EOTF eotf, const tonemappin if ( eotf == EOTF_Gamma22 ) { glm::vec3 val = input; - if ( tonemapping.g22_luminance_fromlinear > 0.f ) + if ( tonemapping.g22_luminance > 0.f ) { - val = glm::clamp( input / tonemapping.g22_luminance_fromlinear, glm::vec3( 0.f ), glm::vec3( 1.f ) ); + val = glm::clamp( input / tonemapping.g22_luminance, glm::vec3( 0.f ), glm::vec3( 1.f ) ); } return glm::pow( val, glm::vec3( 1.f/2.2f ) ); } @@ -241,7 +241,7 @@ glm::vec3 calcLinearToEOTF( const glm::vec3 & input, EOTF eotf, const tonemappin // input is from 0->1 // TODO: use tone-mapping for white, black, contrast ratio -glm::vec3 applyShaper( const glm::vec3 & input, EOTF source, EOTF dest, const tonemapping_t & tonemapping, bool bInvert ) +glm::vec3 applyShaper( const glm::vec3 & input, EOTF source, EOTF dest, const tonemapping_t & tonemapping, float flGain, bool bInvert ) { if ( source == dest || !tonemapping.bUseShaper ) { @@ -252,16 +252,17 @@ glm::vec3 applyShaper( const glm::vec3 & input, EOTF source, EOTF dest, const to { // (once we do tone mapping this gets more complicated std::swap( source, dest ); + flGain = 1.f / flGain; } - return calcLinearToEOTF( calcEOTFToLinear( input, source, tonemapping ), dest, tonemapping ); + return calcLinearToEOTF( flGain * calcEOTFToLinear( input, source, tonemapping ), dest, tonemapping ); } void calcColorTransform( uint16_t * pRgbxData1d, int nLutSize1d, uint16_t * pRgbxData3d, int nLutEdgeSize3d, const displaycolorimetry_t & source, EOTF sourceEOTF, const displaycolorimetry_t & dest, EOTF destEOTF, - const colormapping_t & mapping, const nightmode_t & nightmode, const tonemapping_t & tonemapping ) + const colormapping_t & mapping, const nightmode_t & nightmode, const tonemapping_t & tonemapping, float flGain ) { glm::mat3 xyz_from_dest = normalised_primary_matrix( dest.primaries, dest.white, 1.f ); glm::mat3 dest_from_xyz = glm::inverse( xyz_from_dest ); @@ -281,7 +282,7 @@ void calcColorTransform( uint16_t * pRgbxData1d, int nLutSize1d, for ( int nVal=0; nVal( nInputEOTF ); + float flGain = 1.f; + if ( inputEOTF == EOTF_Gamma22 ) { + flGain = g_ColorMgmt.pending.flSDRInputGain; if ( g_ColorMgmt.pending.outputEncodingEOTF == EOTF_Gamma22 ) { - if ( g_bForceHDRSupportDebug ) - { - tonemapping.g22_luminance_tolinear = g_ColorMgmt.pending.flSDROnHDRBrightness; - tonemapping.g22_luminance_fromlinear = g_ColorMgmt.pending.flInternalDisplayBrightness; - // xwm_log.infof("G22 -> G22 (force HDR) %f %f", tonemapping.g22_luminance_tolinear, tonemapping.g22_luminance_fromlinear ); - } - else - { - // G22 -> G22. Does not matter what the g22 mult is - tonemapping.g22_luminance_tolinear = 1.f; - tonemapping.g22_luminance_fromlinear = 1.f; - // xwm_log.infof("G22 -> G22"); - } - + // G22 -> G22. Does not matter what the g22 mult is + tonemapping.g22_luminance = 1.f; + // xwm_log.infof("G22 -> G22"); } else if ( g_ColorMgmt.pending.outputEncodingEOTF == EOTF_PQ ) { // G22 -> PQ. SDR content going on an HDR output - tonemapping.g22_luminance_tolinear = g_ColorMgmt.pending.flSDROnHDRBrightness; - tonemapping.g22_luminance_fromlinear = g_ColorMgmt.pending.flSDROnHDRBrightness; + tonemapping.g22_luminance = g_ColorMgmt.pending.flSDROnHDRBrightness; // xwm_log.infof("G22 -> PQ"); } @@ -199,18 +190,17 @@ update_color_mgmt() } else if ( inputEOTF == EOTF_PQ ) { + flGain = g_ColorMgmt.pending.flHDRInputGain; if ( g_ColorMgmt.pending.outputEncodingEOTF == EOTF_Gamma22 ) { // PQ -> G22 Leverage the display's native brightness - tonemapping.g22_luminance_tolinear = g_ColorMgmt.pending.flInternalDisplayBrightness; - tonemapping.g22_luminance_fromlinear = g_ColorMgmt.pending.flInternalDisplayBrightness; - // xwm_log.infof("PQ -> 2.2 - tonemapping.g22_luminance %f", tonemapping.g22_luminance_tolinear ); + tonemapping.g22_luminance = g_ColorMgmt.pending.flInternalDisplayBrightness; + // xwm_log.infof("PQ -> 2.2 - tonemapping.g22_luminance %f", tonemapping.g22_luminance ); } else if ( g_ColorMgmt.pending.outputEncodingEOTF == EOTF_PQ ) { // PQ -> PQ. Better not matter what the g22 mult is - tonemapping.g22_luminance_tolinear = 1.f; - tonemapping.g22_luminance_fromlinear = 1.f; + tonemapping.g22_luminance = 1.f; // xwm_log.infof("PQ -> PQ"); } @@ -219,7 +209,7 @@ update_color_mgmt() calcColorTransform( &lut1d[0], nLutSize1d, &lut3d[0], nLutEdgeSize3d, inputColorimetry, inputEOTF, outputEncodingColorimetry, g_ColorMgmt.pending.outputEncodingEOTF, - colorMapping, g_ColorMgmt.pending.nightmode, tonemapping ); + colorMapping, g_ColorMgmt.pending.nightmode, tonemapping, flGain ); if ( !g_ColorMgmtLutsOverride[nInputEOTF].lut3d.empty() && !g_ColorMgmtLutsOverride[nInputEOTF].lut1d.empty() ) { @@ -293,6 +283,35 @@ bool set_sdr_on_hdr_brightness( float flVal ) return g_ColorMgmt.pending.enabled; } +bool set_hdr_input_gain( float flVal ) +{ + if ( flVal < 0.f ) + { + flVal = 1.f; + } + + if ( g_ColorMgmt.pending.flHDRInputGain == flVal ) + return false; + + g_ColorMgmt.pending.flHDRInputGain = flVal; + + return g_ColorMgmt.pending.enabled; +} + +bool set_sdr_input_gain( float flVal ) +{ + if ( flVal < 0.f ) + { + flVal = 1.f; + } + + if ( g_ColorMgmt.pending.flSDRInputGain == flVal ) + return false; + + g_ColorMgmt.pending.flSDRInputGain = flVal; + return g_ColorMgmt.pending.enabled; +} + bool set_color_nightmode( const nightmode_t &nightmode ) { if ( g_ColorMgmt.pending.nightmode == nightmode ) @@ -4953,6 +4972,18 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) if ( set_internal_display_brightness( bit_cast(val) ) ) hasRepaint = true; } + if ( ev->atom == ctx->atoms.gamescopeHDRInputGain ) + { + uint32_t val = get_prop( ctx, ctx->root, ctx->atoms.gamescopeHDRInputGain, 0 ); + if ( set_hdr_input_gain( bit_cast(val) ) ) + hasRepaint = true; + } + if ( ev->atom == ctx->atoms.gamescopeSDRInputGain ) + { + uint32_t val = get_prop( ctx, ctx->root, ctx->atoms.gamescopeSDRInputGain, 0 ); + if ( set_sdr_input_gain( bit_cast(val) ) ) + hasRepaint = true; + } if ( ev->atom == ctx->atoms.gamescopeForceWindowsFullscreen ) { ctx->force_windows_fullscreen = !!get_prop( ctx, ctx->root, ctx->atoms.gamescopeForceWindowsFullscreen, 0 ); @@ -6149,6 +6180,8 @@ void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_ ctx->atoms.gamescopeHDROutputFeedback = XInternAtom( ctx->dpy, "GAMESCOPE_HDR_OUTPUT_FEEDBACK", false ); ctx->atoms.gamescopeSDROnHDRContentBrightness = XInternAtom( ctx->dpy, "GAMESCOPE_SDR_ON_HDR_CONTENT_BRIGHTNESS", false ); ctx->atoms.gamescopeInternalDisplayBrightness = XInternAtom( ctx->dpy, "GAMESCOPE_INTERNAL_DISPLAY_BRIGHTNESS", false ); + ctx->atoms.gamescopeHDRInputGain = XInternAtom( ctx->dpy, "GAMESCOPE_HDR_INPUT_GAIN", false ); + ctx->atoms.gamescopeSDRInputGain = XInternAtom( ctx->dpy, "GAMESCOPE_SDR_INPUT_GAIN", false ); ctx->atoms.gamescopeHDRItmEnable = XInternAtom( ctx->dpy, "GAMESCOPE_HDR_ITM_ENABLE", false ); ctx->atoms.gamescopeHDRItmSDRNits = XInternAtom( ctx->dpy, "GAMESCOPE_HDR_ITM_SDR_NITS", false ); ctx->atoms.gamescopeHDRItmTargetNits = XInternAtom( ctx->dpy, "GAMESCOPE_HDR_ITM_TARGET_NITS", false ); diff --git a/src/xwayland_ctx.hpp b/src/xwayland_ctx.hpp index a6758ce..b3f3454 100644 --- a/src/xwayland_ctx.hpp +++ b/src/xwayland_ctx.hpp @@ -179,6 +179,8 @@ struct xwayland_ctx_t Atom gamescopeHDROutputFeedback; Atom gamescopeSDROnHDRContentBrightness; Atom gamescopeInternalDisplayBrightness; + Atom gamescopeHDRInputGain; + Atom gamescopeSDRInputGain; Atom gamescopeHDRItmEnable; Atom gamescopeHDRItmSDRNits; Atom gamescopeHDRItmTargetNits;