drm, steamcompmgr: Implement 3DLUT + SHAPERLUT support

This commit is contained in:
Joshua Ashton 2023-01-06 15:54:31 +00:00
parent dc6d755ada
commit 5054054768
4 changed files with 244 additions and 0 deletions

View file

@ -590,6 +590,9 @@ static bool refresh_state( drm_t *drm )
if (!crtc->has_vrr_enabled) if (!crtc->has_vrr_enabled)
drm_log.infof("CRTC %" PRIu32 " has no VRR_ENABLED support", crtc->id); 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"]; crtc->current.active = crtc->initial_prop_values["ACTIVE"];
if (crtc->has_vrr_enabled) if (crtc->has_vrr_enabled)
drm->current.vrr_enabled = crtc->initial_prop_values["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); add_crtc_property(req, &drm->crtcs[i], "DEGAMMA_LUT", 0);
if ( drm->crtcs[i].has_ctm ) if ( drm->crtcs[i].has_ctm )
add_crtc_property(req, &drm->crtcs[i], "CTM", 0); 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 ) if ( drm->crtcs[i].has_vrr_enabled )
add_crtc_property(req, &drm->crtcs[i], "VRR_ENABLED", 0); add_crtc_property(req, &drm->crtcs[i], "VRR_ENABLED", 0);
add_crtc_property(req, &drm->crtcs[i], "ACTIVE", 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); drmModeDestroyPropertyBlob(drm->fd, drm->current.gamma_lut_id);
if ( drm->pending.degamma_lut_id != drm->current.degamma_lut_id ) if ( drm->pending.degamma_lut_id != drm->current.degamma_lut_id )
drmModeDestroyPropertyBlob(drm->fd, 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; 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_degamma_lut(drm);
drm_update_color_mtx(drm); drm_update_color_mtx(drm);
drm_update_vrr_state(drm); drm_update_vrr_state(drm);
drm_update_lut3d(drm);
drm_update_shaperlut(drm);
drm->fbids_in_req.clear(); 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) if (ret < 0)
return ret; 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) if (crtc->has_vrr_enabled)
{ {
int ret = add_crtc_property(drm->req, crtc, "VRR_ENABLED", 0); 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; 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) if (drm->crtc->has_vrr_enabled)
{ {
ret = add_crtc_property(drm->req, drm->crtc, "VRR_ENABLED", drm->pending.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; 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 ) 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 ); 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; 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<std::vector<drm_color_lut>>(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<std::vector<drm_color_lut>>(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) void drm_set_vrr_enabled(struct drm_t *drm, bool enabled)
{ {
drm->wants_vrr_enabled = enabled; drm->wants_vrr_enabled = enabled;
@ -2268,6 +2381,88 @@ bool drm_update_color_mtx(struct drm_t *drm)
return true; 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) bool drm_update_vrr_state(struct drm_t *drm)
{ {
drm->pending.vrr_enabled = false; drm->pending.vrr_enabled = false;

View file

@ -72,6 +72,8 @@ struct crtc {
bool has_degamma_lut; bool has_degamma_lut;
bool has_ctm; bool has_ctm;
bool has_vrr_enabled; bool has_vrr_enabled;
uint32_t lut3d_size;
uint32_t shaperlut_size;
struct { struct {
bool active; bool active;
@ -159,6 +161,8 @@ struct drm_t {
uint32_t gamma_lut_id; uint32_t gamma_lut_id;
uint32_t degamma_lut_id; uint32_t degamma_lut_id;
uint32_t ctm_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_gain[3] = { 1.0f, 1.0f, 1.0f };
float color_linear_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 } }; 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; float gain_blend = 0.0f;
enum drm_screen_type screen_type = DRM_SCREEN_TYPE_INTERNAL; enum drm_screen_type screen_type = DRM_SCREEN_TYPE_INTERNAL;
bool vrr_enabled = false; bool vrr_enabled = false;
std::shared_ptr<std::vector<drm_color_lut>> color_lut3d[DRM_SCREEN_TYPE_COUNT];
std::shared_ptr<std::vector<drm_color_lut>> color_shaperlut[DRM_SCREEN_TYPE_COUNT];
} current, pending; } current, pending;
bool wants_vrr_enabled = false; 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_gamma_lut(struct drm_t *drm);
bool drm_update_degamma_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_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_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_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); 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<uint32_t, uint32_t> drm_get_connector_identifier(struct drm_t *drm); std::pair<uint32_t, uint32_t> drm_get_connector_identifier(struct drm_t *drm);
void drm_set_hdr_state(struct drm_t *drm, bool enabled); 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; extern bool g_bSupportsAsyncFlips;
/* from CTA-861-G */ /* from CTA-861-G */

View file

@ -2221,6 +2221,17 @@ bool get_prop( xwayland_ctx_t *ctx, Window win, Atom prop, std::vector< uint32_t
return false; 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<const char *>( tp.value );
XFree( tp.value );
return value;
}
static bool static bool
win_has_game_id( win *w ) win_has_game_id( win *w )
{ {
@ -4484,6 +4495,24 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev)
g_flLinearToNits = 400.0f; g_flLinearToNits = 400.0f;
hasRepaint = true; 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) if (ev->atom == ctx->atoms.wineHwndStyle)
{ {
win * w = find_win(ctx, ev->window); 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.gamescopeHDROutputFeedback = XInternAtom( ctx->dpy, "GAMESCOPE_HDR_OUTPUT_FEEDBACK", false );
ctx->atoms.gamescopeHDRSDRContentBrightness = XInternAtom( ctx->dpy, "GAMESCOPE_HDR_SDR_CONTENT_BRIGHTNESS", 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.wineHwndStyle = XInternAtom( ctx->dpy, "_WINE_HWND_STYLE", false );
ctx->atoms.wineHwndStyleEx = XInternAtom( ctx->dpy, "_WINE_HWND_EXSTYLE", false ); ctx->atoms.wineHwndStyleEx = XInternAtom( ctx->dpy, "_WINE_HWND_EXSTYLE", false );

View file

@ -179,6 +179,9 @@ struct xwayland_ctx_t
Atom gamescopeHDROutputFeedback; Atom gamescopeHDROutputFeedback;
Atom gamescopeHDRSDRContentBrightness; Atom gamescopeHDRSDRContentBrightness;
Atom gamescopeColorLut3D[DRM_SCREEN_TYPE_COUNT];
Atom gamescopeColorShaperLut[DRM_SCREEN_TYPE_COUNT];
Atom wineHwndStyle; Atom wineHwndStyle;
Atom wineHwndStyleEx; Atom wineHwndStyleEx;
} atoms; } atoms;