diff --git a/src/drm.cpp b/src/drm.cpp index 7d3cf6d..c04d99e 100644 --- a/src/drm.cpp +++ b/src/drm.cpp @@ -416,6 +416,9 @@ static bool refresh_state( drm_t *drm ) crtc->has_gamma_lut = (crtc->props.find( "GAMMA_LUT" ) != crtc->props.end()); if (!crtc->has_gamma_lut) drm_log.infof("CRTC %" PRIu32 " has no gamma LUT support", crtc->id); + crtc->has_degamma_lut = (crtc->props.find( "DEGAMMA_LUT" ) != crtc->props.end()); + if (!crtc->has_degamma_lut) + drm_log.infof("CRTC %" PRIu32 " has no degamma LUT support", crtc->id); crtc->has_ctm = (crtc->props.find( "CTM" ) != crtc->props.end()); if (!crtc->has_ctm) drm_log.infof("CRTC %" PRIu32 " has no CTM support", crtc->id); @@ -788,6 +791,8 @@ void finish_drm(struct drm_t *drm) add_crtc_property(req, &drm->crtcs[i], "MODE_ID", 0); if ( drm->crtcs[i].has_gamma_lut ) add_crtc_property(req, &drm->crtcs[i], "GAMMA_LUT", 0); + if ( drm->crtcs[i].has_degamma_lut ) + add_crtc_property(req, &drm->crtcs[i], "DEGAMMA_LUT", 0); if ( drm->crtcs[i].has_ctm ) add_crtc_property(req, &drm->crtcs[i], "CTM", 0); add_crtc_property(req, &drm->crtcs[i], "ACTIVE", 0); @@ -899,6 +904,8 @@ int drm_commit(struct drm_t *drm, const struct FrameInfo_t *frameInfo ) drmModeDestroyPropertyBlob(drm->fd, drm->current.mode_id); if ( drm->pending.gamma_lut_id != drm->current.gamma_lut_id ) drmModeDestroyPropertyBlob(drm->fd, drm->current.gamma_lut_id); + if ( drm->pending.degamma_lut_id != drm->current.degamma_lut_id ) + drmModeDestroyPropertyBlob(drm->fd, drm->current.degamma_lut_id); drm->crtcs[i].current = drm->crtcs[i].pending; } } @@ -1238,6 +1245,7 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo ) int drm_prepare( struct drm_t *drm, const struct FrameInfo_t *frameInfo ) { drm_update_gamma_lut(drm); + drm_update_degamma_lut(drm); drm_update_color_mtx(drm); drm->fbids_in_req.clear(); @@ -1275,6 +1283,11 @@ int drm_prepare( struct drm_t *drm, const struct FrameInfo_t *frameInfo ) if (add_crtc_property(drm->req, &drm->crtcs[i], "GAMMA_LUT", 0) < 0) return false; } + if (drm->crtcs[i].has_degamma_lut) + { + if (add_crtc_property(drm->req, &drm->crtcs[i], "DEGAMMA_LUT", 0) < 0) + return false; + } if (drm->crtcs[i].has_ctm) { if (add_crtc_property(drm->req, &drm->crtcs[i], "CTM", 0) < 0) @@ -1299,6 +1312,12 @@ int drm_prepare( struct drm_t *drm, const struct FrameInfo_t *frameInfo ) return false; } + if (drm->crtc->has_degamma_lut) + { + if (add_crtc_property(drm->req, drm->crtc, "DEGAMMA_LUT", drm->pending.degamma_lut_id) < 0) + return false; + } + if (drm->crtc->has_ctm) { if (add_crtc_property(drm->req, drm->crtc, "CTM", drm->pending.ctm_id) < 0) @@ -1317,6 +1336,12 @@ int drm_prepare( struct drm_t *drm, const struct FrameInfo_t *frameInfo ) return false; } + if ( drm->crtc->has_degamma_lut && drm->pending.degamma_lut_id != drm->current.degamma_lut_id ) + { + if (add_crtc_property(drm->req, drm->crtc, "DEGAMMA_LUT", drm->pending.degamma_lut_id) < 0) + return false; + } + if ( drm->crtc->has_ctm && drm->pending.ctm_id != drm->current.ctm_id ) { if (add_crtc_property(drm->req, drm->crtc, "CTM", drm->pending.ctm_id) < 0) @@ -1487,15 +1512,46 @@ bool drm_set_color_gain_blend(struct drm_t *drm, float blend) return false; } +bool drm_set_gamma_exponent(struct drm_t *drm, float *vec) +{ + for (int i = 0; i < 3; i++) + drm->pending.color_gamma_exponent[i] = vec[i]; + + for (int i = 0; i < 3; i++) + { + if ( drm->current.color_gamma_exponent[i] != drm->pending.color_gamma_exponent[i] ) + return true; + } + return false; +} + +bool drm_set_degamma_exponent(struct drm_t *drm, float *vec) +{ + for (int i = 0; i < 3; i++) + drm->pending.color_degamma_exponent[i] = vec[i]; + + for (int i = 0; i < 3; i++) + { + if ( drm->current.color_degamma_exponent[i] != drm->pending.color_degamma_exponent[i] ) + return true; + } + return false; +} + inline float lerp( float a, float b, float t ) { return a + t * (b - a); } +inline uint16_t drm_quantize_lut_value( float flValue ) +{ + return (uint16_t)quantize( flValue, (float)UINT16_MAX ); +} + inline uint16_t drm_calc_lut_value( float input, float flLinearGain, float flGain, float flBlend ) { float flValue = lerp( flGain * input, linear_to_srgb( flLinearGain * srgb_to_linear( input ) ), flBlend ); - return (uint16_t)quantize( flValue, (float)UINT16_MAX ); + return drm_quantize_lut_value( flValue ); } bool drm_update_color_mtx(struct drm_t *drm) @@ -1572,6 +1628,15 @@ bool drm_update_color_mtx(struct drm_t *drm) return true; } +static float safe_pow(float x, float y) +{ + // Avoids pow(x, 1.0f) != x. + if (y == 1.0f) + return x; + + return pow(x, y); +} + bool drm_update_gamma_lut(struct drm_t *drm) { if ( !drm->crtc->has_gamma_lut ) @@ -1583,6 +1648,9 @@ bool drm_update_gamma_lut(struct drm_t *drm) drm->pending.color_linear_gain[0] == drm->current.color_linear_gain[0] && drm->pending.color_linear_gain[1] == drm->current.color_linear_gain[1] && drm->pending.color_linear_gain[2] == drm->current.color_linear_gain[2] && + drm->pending.color_gamma_exponent[0] == drm->current.color_gamma_exponent[0] && + drm->pending.color_gamma_exponent[1] == drm->current.color_gamma_exponent[1] && + drm->pending.color_gamma_exponent[2] == drm->current.color_gamma_exponent[2] && drm->pending.gain_blend == drm->current.gain_blend ) { return true; @@ -1598,7 +1666,12 @@ bool drm_update_gamma_lut(struct drm_t *drm) drm->pending.color_linear_gain[1] == 1.0f && drm->pending.color_linear_gain[2] == 1.0f ); - if ( color_gain_identity && linear_gain_identity ) + bool gamma_exponent_identity = + ( drm->pending.color_gamma_exponent[0] == 1.0f && + drm->pending.color_gamma_exponent[1] == 1.0f && + drm->pending.color_gamma_exponent[2] == 1.0f ); + + if ( color_gain_identity && linear_gain_identity && gamma_exponent_identity ) { drm->pending.gamma_lut_id = 0; return true; @@ -1609,9 +1682,14 @@ bool drm_update_gamma_lut(struct drm_t *drm) for ( int i = 0; i < lut_entries; i++ ) { float input = float(i) / float(lut_entries - 1); - gamma_lut[i].red = drm_calc_lut_value( input, drm->pending.color_linear_gain[0], drm->pending.color_gain[0], drm->pending.gain_blend ); - gamma_lut[i].green = drm_calc_lut_value( input, drm->pending.color_linear_gain[1], drm->pending.color_gain[1], drm->pending.gain_blend ); - gamma_lut[i].blue = drm_calc_lut_value( input, drm->pending.color_linear_gain[2], drm->pending.color_gain[2], drm->pending.gain_blend ); + + float r_exp = safe_pow( input, drm->pending.color_gamma_exponent[0] ); + float g_exp = safe_pow( input, drm->pending.color_gamma_exponent[1] ); + float b_exp = safe_pow( input, drm->pending.color_gamma_exponent[2] ); + + gamma_lut[i].red = drm_calc_lut_value( r_exp, drm->pending.color_linear_gain[0], drm->pending.color_gain[0], drm->pending.gain_blend ); + gamma_lut[i].green = drm_calc_lut_value( g_exp, drm->pending.color_linear_gain[1], drm->pending.color_gain[1], drm->pending.gain_blend ); + gamma_lut[i].blue = drm_calc_lut_value( b_exp, drm->pending.color_linear_gain[2], drm->pending.color_gain[2], drm->pending.gain_blend ); } uint32_t blob_id = 0; @@ -1628,6 +1706,54 @@ bool drm_update_gamma_lut(struct drm_t *drm) return true; } +bool drm_update_degamma_lut(struct drm_t *drm) +{ + if ( !drm->crtc->has_degamma_lut ) + return true; + + if (drm->pending.color_degamma_exponent[0] == drm->current.color_degamma_exponent[0] && + drm->pending.color_degamma_exponent[1] == drm->current.color_degamma_exponent[1] && + drm->pending.color_degamma_exponent[2] == drm->current.color_degamma_exponent[2]) + { + return true; + } + + bool degamma_exponent_identity = + ( drm->pending.color_degamma_exponent[0] == 1.0f && + drm->pending.color_degamma_exponent[1] == 1.0f && + drm->pending.color_degamma_exponent[2] == 1.0f ); + + if ( degamma_exponent_identity ) + { + drm->pending.degamma_lut_id = 0; + return true; + } + + const int lut_entries = drm->crtc->initial_prop_values["DEGAMMA_LUT_SIZE"]; + drm_color_lut *degamma_lut = new drm_color_lut[lut_entries]; + for ( int i = 0; i < lut_entries; i++ ) + { + float input = float(i) / float(lut_entries - 1); + + degamma_lut[i].red = drm_quantize_lut_value( safe_pow( input, drm->pending.color_degamma_exponent[0] ) ); + degamma_lut[i].green = drm_quantize_lut_value( safe_pow( input, drm->pending.color_degamma_exponent[1] ) ); + degamma_lut[i].blue = drm_quantize_lut_value( safe_pow( input, drm->pending.color_degamma_exponent[2] ) ); + } + + uint32_t blob_id = 0; + if (drmModeCreatePropertyBlob(drm->fd, degamma_lut, + lut_entries * sizeof(struct drm_color_lut), &blob_id) != 0) { + drm_log.errorf_errno("Unable to create degamma LUT property blob"); + delete[] degamma_lut; + return false; + } + delete[] degamma_lut; + + drm->pending.degamma_lut_id = blob_id; + + return true; +} + bool drm_set_mode( struct drm_t *drm, const drmModeModeInfo *mode ) { uint32_t mode_id = 0; diff --git a/src/drm.hpp b/src/drm.hpp index 481182f..21ed839 100644 --- a/src/drm.hpp +++ b/src/drm.hpp @@ -36,6 +36,7 @@ struct crtc { std::map props; std::map initial_prop_values; bool has_gamma_lut; + bool has_degamma_lut; bool has_ctm; struct { @@ -98,9 +99,12 @@ struct drm_t { struct { uint32_t mode_id; uint32_t gamma_lut_id; + uint32_t degamma_lut_id; uint32_t ctm_id; float color_gain[3] = { 1.0f, 1.0f, 1.0f }; float color_linear_gain[3] = { 1.0f, 1.0f, 1.0f }; + float color_gamma_exponent[3] = { 1.0f, 1.0f, 1.0f }; + float color_degamma_exponent[3] = { 1.0f, 1.0f, 1.0f }; float color_mtx[9] = { 1.0f, 0.0f, 0.0f, @@ -141,6 +145,7 @@ extern uint32_t g_nDRMFormat; extern bool g_bUseLayers; extern bool g_bRotated; +extern bool g_bFlipped; extern bool g_bDebugLayers; extern const char *g_sOutputName; @@ -170,7 +175,10 @@ bool drm_set_color_gains(struct drm_t *drm, float *gains); bool drm_set_color_mtx(struct drm_t *drm, float *mtx); bool drm_set_color_gain_blend(struct drm_t *drm, float blend); bool drm_update_gamma_lut(struct drm_t *drm); +bool drm_update_degamma_lut(struct drm_t *drm); bool drm_update_color_mtx(struct drm_t *drm); +bool drm_set_gamma_exponent(struct drm_t *drm, float *vec); +bool drm_set_degamma_exponent(struct drm_t *drm, float *vec); char *find_drm_node_by_devid(dev_t devid); int drm_get_default_refresh(struct drm_t *drm); diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index f5679ae..d16b893 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -3967,6 +3967,25 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) if ( drm_set_color_gain_blend( &g_DRM, flBlend ) ) hasRepaint = true; } + if ( ev->atom == ctx->atoms.gamescopeColorGammaExponent ) + { + std::vector< uint32_t > user_vec; + bool bHasVec = get_prop( ctx, ctx->root, ctx->atoms.gamescopeColorGammaExponent, user_vec ); + + // identity + float vec[6] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; + if ( bHasVec && user_vec.size() == 6 ) + { + for (int i = 0; i < 6; i++) + vec[i] = bit_cast( user_vec[i] ); + } + + if ( drm_set_degamma_exponent( &g_DRM, &vec[0] ) ) + hasRepaint = true; + + if ( drm_set_gamma_exponent( &g_DRM, &vec[3] ) ) + hasRepaint = true; + } if ( ev->atom == ctx->atoms.gamescopeXWaylandModeControl ) { std::vector< uint32_t > xwayland_mode_ctl; @@ -4913,6 +4932,9 @@ void init_xwayland_ctx(gamescope_xwayland_server_t *xwayland_server) ctx->atoms.gamescopeColorGain = XInternAtom( ctx->dpy, "GAMESCOPE_COLOR_GAIN", false ); ctx->atoms.gamescopeColorMatrix = XInternAtom( ctx->dpy, "GAMESCOPE_COLOR_MATRIX", false ); ctx->atoms.gamescopeColorLinearGainBlend = XInternAtom( ctx->dpy, "GAMESCOPE_COLOR_LINEARGAIN_BLEND", false ); + + ctx->atoms.gamescopeColorGammaExponent = XInternAtom( ctx->dpy, "GAMESCOPE_COLOR_GAMMA_EXPONENT", false ); + ctx->atoms.gamescopeXWaylandModeControl = XInternAtom( ctx->dpy, "GAMESCOPE_XWAYLAND_MODE_CONTROL", false ); ctx->atoms.gamescopeFPSLimit = XInternAtom( ctx->dpy, "GAMESCOPE_FPS_LIMIT", false ); ctx->atoms.gamescopeDynamicRefresh = XInternAtom( ctx->dpy, "GAMESCOPE_DYNAMIC_REFRESH", false ); diff --git a/src/xwayland_ctx.hpp b/src/xwayland_ctx.hpp index d5c0795..f7ab011 100644 --- a/src/xwayland_ctx.hpp +++ b/src/xwayland_ctx.hpp @@ -124,6 +124,8 @@ struct xwayland_ctx_t Atom gamescopeColorMatrix; Atom gamescopeColorLinearGainBlend; + Atom gamescopeColorGammaExponent; + Atom gamescopeXWaylandModeControl; Atom gamescopeFPSLimit;