From 50540547680965cf424fb483bc68208f43c4e123 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Fri, 6 Jan 2023 15:54:31 +0000 Subject: [PATCH] drm, steamcompmgr: Implement 3DLUT + SHAPERLUT support --- src/drm.cpp | 195 +++++++++++++++++++++++++++++++++++++++++++ src/drm.hpp | 11 +++ src/steamcompmgr.cpp | 35 ++++++++ src/xwayland_ctx.hpp | 3 + 4 files changed, 244 insertions(+) diff --git a/src/drm.cpp b/src/drm.cpp index c9376b7..22bfa70 100644 --- a/src/drm.cpp +++ b/src/drm.cpp @@ -590,6 +590,9 @@ static bool refresh_state( drm_t *drm ) if (!crtc->has_vrr_enabled) drm_log.infof("CRTC %" PRIu32 " has no VRR_ENABLED support", crtc->id); + crtc->lut3d_size = crtc->props.contains( "VALVE1_LUT3D_SIZE" ) ? uint32_t(crtc->initial_prop_values["VALVE1_LUT3D_SIZE"]) : 0; + crtc->shaperlut_size = crtc->props.contains( "VALVE1_SHAPER_LUT_SIZE" ) ? uint32_t(crtc->initial_prop_values["VALVE1_SHAPER_LUT_SIZE"]) : 0; + crtc->current.active = crtc->initial_prop_values["ACTIVE"]; if (crtc->has_vrr_enabled) drm->current.vrr_enabled = crtc->initial_prop_values["VRR_ENABLED"]; @@ -1055,6 +1058,10 @@ void finish_drm(struct drm_t *drm) 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); + if ( drm->crtcs[i].lut3d_size ) + add_crtc_property(req, &drm->crtcs[i], "VALVE1_LUT3D", 0); + if ( drm->crtcs[i].shaperlut_size ) + add_crtc_property(req, &drm->crtcs[i], "VALVE1_SHAPER_LUT", 0); if ( drm->crtcs[i].has_vrr_enabled ) add_crtc_property(req, &drm->crtcs[i], "VRR_ENABLED", 0); add_crtc_property(req, &drm->crtcs[i], "ACTIVE", 0); @@ -1175,6 +1182,10 @@ int drm_commit(struct drm_t *drm, const struct FrameInfo_t *frameInfo ) 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); + if ( drm->pending.lut3d_id != drm->current.lut3d_id ) + drmModeDestroyPropertyBlob(drm->fd, drm->current.lut3d_id); + if ( drm->pending.shaperlut_id != drm->current.shaperlut_id ) + drmModeDestroyPropertyBlob(drm->fd, drm->current.shaperlut_id); drm->crtcs[i].current = drm->crtcs[i].pending; } @@ -1755,6 +1766,8 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI drm_update_degamma_lut(drm); drm_update_color_mtx(drm); drm_update_vrr_state(drm); + drm_update_lut3d(drm); + drm_update_shaperlut(drm); drm->fbids_in_req.clear(); @@ -1827,6 +1840,18 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI if (ret < 0) return ret; } + if (crtc->lut3d_size) + { + int ret = add_crtc_property(drm->req, crtc, "VALVE1_LUT3D", 0); + if (ret < 0) + return ret; + } + if (crtc->shaperlut_size) + { + int ret = add_crtc_property(drm->req, crtc, "VALVE1_SHAPER_LUT", 0); + if (ret < 0) + return ret; + } if (crtc->has_vrr_enabled) { int ret = add_crtc_property(drm->req, crtc, "VRR_ENABLED", 0); @@ -1899,6 +1924,20 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI return false; } + if (drm->crtc->lut3d_size) + { + ret = add_crtc_property(drm->req, drm->crtc, "VALVE1_LUT3D", drm->pending.lut3d_id); + if (ret < 0) + return false; + } + + if (drm->crtc->shaperlut_size) + { + ret = add_crtc_property(drm->req, drm->crtc, "VALVE1_SHAPER_LUT", drm->pending.shaperlut_id); + if (ret < 0) + return false; + } + if (drm->crtc->has_vrr_enabled) { ret = add_crtc_property(drm->req, drm->crtc, "VRR_ENABLED", drm->pending.vrr_enabled); @@ -1934,6 +1973,20 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI return ret; } + if ( drm->crtc->lut3d_size && drm->pending.lut3d_id != drm->current.lut3d_id ) + { + int ret = add_crtc_property(drm->req, drm->crtc, "VALVE1_LUT3D", drm->pending.lut3d_id); + if (ret < 0) + return ret; + } + + if ( drm->crtc->shaperlut_size && drm->pending.shaperlut_id != drm->current.shaperlut_id ) + { + int ret = add_crtc_property(drm->req, drm->crtc, "VALVE1_SHAPER_LUT", drm->pending.shaperlut_id); + if (ret < 0) + return ret; + } + if ( drm->crtc->has_vrr_enabled && drm->pending.vrr_enabled != drm->current.vrr_enabled ) { int ret = add_crtc_property(drm->req, drm->crtc, "VRR_ENABLED", drm->pending.vrr_enabled ); @@ -2105,6 +2158,66 @@ bool drm_set_color_mtx(struct drm_t *drm, float *mtx, enum drm_screen_type scree return false; } +bool drm_set_3dlut(struct drm_t *drm, const char *path, enum drm_screen_type screen_type) +{ + bool current_screen = drm_get_screen_type(drm) == screen_type; + bool had_lut = drm->current.color_lut3d[screen_type] != nullptr; + + drm->pending.color_lut3d[screen_type] = nullptr; + + FILE *f = fopen(path, "rb"); + if (!f) { + return had_lut && current_screen; + } + + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + size_t elems = fsize / sizeof(drm_color_lut); + + if (elems == 0) { + return had_lut && current_screen; + } + + auto data = std::make_shared>(elems); + fread(data->data(), elems, sizeof(drm_color_lut), f); + + drm->pending.color_lut3d[screen_type] = std::move(data); + + return current_screen; +} + +bool drm_set_shaperlut(struct drm_t *drm, const char *path, enum drm_screen_type screen_type) +{ + bool current_screen = drm_get_screen_type(drm) == screen_type; + bool had_lut = drm->current.color_shaperlut[screen_type] != nullptr; + + drm->pending.color_shaperlut[screen_type] = nullptr; + + FILE *f = fopen(path, "rb"); + if (!f) { + return had_lut && current_screen; + } + + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + size_t elems = fsize / sizeof(drm_color_lut); + + if (elems == 0) { + return had_lut && current_screen; + } + + auto data = std::make_shared>(elems); + fread(data->data(), elems, sizeof(drm_color_lut), f); + + drm->pending.color_shaperlut[screen_type] = std::move(data); + + return current_screen; +} + void drm_set_vrr_enabled(struct drm_t *drm, bool enabled) { drm->wants_vrr_enabled = enabled; @@ -2268,6 +2381,88 @@ bool drm_update_color_mtx(struct drm_t *drm) return true; } +bool drm_update_lut3d(struct drm_t *drm) +{ + if ( !drm->crtc->lut3d_size ) + return true; + + bool dirty = false; + + enum drm_screen_type screen_type = drm->pending.screen_type; + + if (drm->pending.screen_type != drm->current.screen_type) + dirty = true; + + if (drm->pending.color_lut3d[screen_type] != drm->current.color_lut3d[screen_type]) + dirty = true; + + if (!dirty) + return true; + + // Don't actually check for this, it's a lot of values... + // Just check if it's empty or missing. + bool identity = + !drm->pending.color_lut3d[screen_type] || + drm->pending.color_lut3d[screen_type]->empty(); + + if (identity) + { + drm->pending.lut3d_id = 0; + return true; + } + + uint32_t blob_id = 0; + if (drmModeCreatePropertyBlob(drm->fd, drm->pending.color_lut3d[screen_type]->data(), + sizeof(drm_color_lut) * drm->pending.color_lut3d[screen_type]->size(), &blob_id) != 0) { + drm_log.errorf_errno("Unable to create LUT3D property blob"); + return false; + } + + drm->pending.lut3d_id = blob_id; + return true; +} + +bool drm_update_shaperlut(struct drm_t *drm) +{ + if ( !drm->crtc->shaperlut_size ) + return true; + + bool dirty = false; + + enum drm_screen_type screen_type = drm->pending.screen_type; + + if (drm->pending.screen_type != drm->current.screen_type) + dirty = true; + + if (drm->pending.color_shaperlut[screen_type] != drm->current.color_shaperlut[screen_type]) + dirty = true; + + if (!dirty) + return true; + + // Don't actually check for this, it's a lot of values... + // Just check if it's empty or missing. + bool identity = + !drm->pending.color_shaperlut[screen_type] || + drm->pending.color_shaperlut[screen_type]->empty(); + + if (identity) + { + drm->pending.shaperlut_id = 0; + return true; + } + + uint32_t blob_id = 0; + if (drmModeCreatePropertyBlob(drm->fd, drm->pending.color_shaperlut[screen_type]->data(), + sizeof(drm_color_lut) * drm->pending.color_shaperlut[screen_type]->size(), &blob_id) != 0) { + drm_log.errorf_errno("Unable to create SHAPERLUT property blob"); + return false; + } + + drm->pending.shaperlut_id = blob_id; + return true; +} + bool drm_update_vrr_state(struct drm_t *drm) { drm->pending.vrr_enabled = false; diff --git a/src/drm.hpp b/src/drm.hpp index 5cca776..f85348a 100644 --- a/src/drm.hpp +++ b/src/drm.hpp @@ -72,6 +72,8 @@ struct crtc { bool has_degamma_lut; bool has_ctm; bool has_vrr_enabled; + uint32_t lut3d_size; + uint32_t shaperlut_size; struct { bool active; @@ -159,6 +161,8 @@ struct drm_t { uint32_t gamma_lut_id; uint32_t degamma_lut_id; uint32_t ctm_id; + uint32_t lut3d_id; + uint32_t shaperlut_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[DRM_SCREEN_TYPE_COUNT][3] = { { 1.0f, 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f } }; @@ -180,6 +184,8 @@ struct drm_t { float gain_blend = 0.0f; enum drm_screen_type screen_type = DRM_SCREEN_TYPE_INTERNAL; bool vrr_enabled = false; + std::shared_ptr> color_lut3d[DRM_SCREEN_TYPE_COUNT]; + std::shared_ptr> color_shaperlut[DRM_SCREEN_TYPE_COUNT]; } current, pending; bool wants_vrr_enabled = false; @@ -263,6 +269,8 @@ 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_update_lut3d(struct drm_t *drm); +bool drm_update_shaperlut(struct drm_t *drm); bool drm_update_vrr_state(struct drm_t *drm); bool drm_set_gamma_exponent(struct drm_t *drm, float *vec, enum drm_screen_type screen_type); bool drm_set_degamma_exponent(struct drm_t *drm, float *vec, enum drm_screen_type screen_type); @@ -283,6 +291,9 @@ const char *drm_get_device_name(struct drm_t *drm); std::pair drm_get_connector_identifier(struct drm_t *drm); void drm_set_hdr_state(struct drm_t *drm, bool enabled); +bool drm_set_3dlut(struct drm_t *drm, const char *path, enum drm_screen_type screen_type); +bool drm_set_shaperlut(struct drm_t *drm, const char *path, enum drm_screen_type screen_type); + extern bool g_bSupportsAsyncFlips; /* from CTA-861-G */ diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 6c0449b..f0ab2e9 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -2221,6 +2221,17 @@ bool get_prop( xwayland_ctx_t *ctx, Window win, Atom prop, std::vector< uint32_t return false; } +std::string get_string_prop( xwayland_ctx_t *ctx, Window win, Atom prop ) +{ + XTextProperty tp; + if ( !XGetTextProperty( ctx->dpy, win, &tp, prop ) ) + return ""; + + std::string value = reinterpret_cast( tp.value ); + XFree( tp.value ); + return value; +} + static bool win_has_game_id( win *w ) { @@ -4484,6 +4495,24 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) g_flLinearToNits = 400.0f; hasRepaint = true; } + for (int i = 0; i < DRM_SCREEN_TYPE_COUNT; i++) + { + if ( ev->atom == ctx->atoms.gamescopeColorLut3D[i] ) + { + std::string path = get_string_prop( ctx, ctx->root, ctx->atoms.gamescopeColorLut3D[i] ); + if ( drm_set_3dlut( &g_DRM, path.c_str(), drm_screen_type(i) ) ) + hasRepaint = true; + } + } + for (int i = 0; i < DRM_SCREEN_TYPE_COUNT; i++) + { + if ( ev->atom == ctx->atoms.gamescopeColorShaperLut[i] ) + { + std::string path = get_string_prop( ctx, ctx->root, ctx->atoms.gamescopeColorShaperLut[i] ); + if ( drm_set_shaperlut( &g_DRM, path.c_str(), drm_screen_type(i) ) ) + hasRepaint = true; + } + } if (ev->atom == ctx->atoms.wineHwndStyle) { win * w = find_win(ctx, ev->window); @@ -5456,6 +5485,12 @@ void init_xwayland_ctx(gamescope_xwayland_server_t *xwayland_server) ctx->atoms.gamescopeHDROutputFeedback = XInternAtom( ctx->dpy, "GAMESCOPE_HDR_OUTPUT_FEEDBACK", false ); ctx->atoms.gamescopeHDRSDRContentBrightness = XInternAtom( ctx->dpy, "GAMESCOPE_HDR_SDR_CONTENT_BRIGHTNESS", false ); + ctx->atoms.gamescopeColorLut3D[DRM_SCREEN_TYPE_INTERNAL] = XInternAtom( ctx->dpy, "GAMESCOPE_COLOR_3DLUT", false ); + ctx->atoms.gamescopeColorLut3D[DRM_SCREEN_TYPE_EXTERNAL] = XInternAtom( ctx->dpy, "GAMESCOPE_COLOR_3DLUT_EXTERNAL", false ); + + ctx->atoms.gamescopeColorShaperLut[DRM_SCREEN_TYPE_INTERNAL] = XInternAtom( ctx->dpy, "GAMESCOPE_COLOR_SHAPERLUT", false ); + ctx->atoms.gamescopeColorShaperLut[DRM_SCREEN_TYPE_EXTERNAL] = XInternAtom( ctx->dpy, "GAMESCOPE_COLOR_SHAPERLUT_EXTERNAL", false ); + ctx->atoms.wineHwndStyle = XInternAtom( ctx->dpy, "_WINE_HWND_STYLE", false ); ctx->atoms.wineHwndStyleEx = XInternAtom( ctx->dpy, "_WINE_HWND_EXSTYLE", false ); diff --git a/src/xwayland_ctx.hpp b/src/xwayland_ctx.hpp index 01a40a8..91ed81b 100644 --- a/src/xwayland_ctx.hpp +++ b/src/xwayland_ctx.hpp @@ -179,6 +179,9 @@ struct xwayland_ctx_t Atom gamescopeHDROutputFeedback; Atom gamescopeHDRSDRContentBrightness; + Atom gamescopeColorLut3D[DRM_SCREEN_TYPE_COUNT]; + Atom gamescopeColorShaperLut[DRM_SCREEN_TYPE_COUNT]; + Atom wineHwndStyle; Atom wineHwndStyleEx; } atoms;