drm: Implement linear color gain control via atoms
Adds GAMESCOPE_COLOR_LINEARGAIN to control the linear color gain.
This commit is contained in:
parent
55cb27fd15
commit
c5ccd71ff6
4 changed files with 114 additions and 0 deletions
83
src/drm.cpp
83
src/drm.cpp
|
@ -739,6 +739,7 @@ void finish_drm(struct drm_t *drm)
|
|||
}
|
||||
for ( size_t i = 0; i < drm->crtcs.size(); i++ ) {
|
||||
add_crtc_property(req, &drm->crtcs[i], "MODE_ID", 0);
|
||||
add_crtc_property(req, &drm->crtcs[i], "GAMMA_LUT", 0);
|
||||
add_crtc_property(req, &drm->crtcs[i], "ACTIVE", 0);
|
||||
}
|
||||
for ( size_t i = 0; i < drm->planes.size(); i++ ) {
|
||||
|
@ -846,6 +847,8 @@ int drm_commit(struct drm_t *drm, struct Composite_t *pComposite, struct VulkanP
|
|||
{
|
||||
if ( drm->current.mode_id )
|
||||
drmModeDestroyPropertyBlob(drm->fd, drm->current.mode_id);
|
||||
if ( drm->current.gamma_lut_id )
|
||||
drmModeDestroyPropertyBlob(drm->fd, drm->current.gamma_lut_id);
|
||||
drm->crtcs[i].current = drm->crtcs[i].pending;
|
||||
}
|
||||
}
|
||||
|
@ -1184,6 +1187,8 @@ drm_prepare_liftoff( struct drm_t *drm, const struct Composite_t *pComposite, co
|
|||
* error or if the scene-graph can't be presented directly. */
|
||||
int drm_prepare( struct drm_t *drm, const struct Composite_t *pComposite, const struct VulkanPipeline_t *pPipeline )
|
||||
{
|
||||
drm_update_gamma_lut(drm);
|
||||
|
||||
drm->fbids_in_req.clear();
|
||||
|
||||
bool needs_modeset = drm->needs_modeset.exchange(false);
|
||||
|
@ -1213,6 +1218,8 @@ int drm_prepare( struct drm_t *drm, const struct Composite_t *pComposite, const
|
|||
|
||||
if (add_crtc_property(drm->req, &drm->crtcs[i], "MODE_ID", 0) < 0)
|
||||
return false;
|
||||
if (add_crtc_property(drm->req, &drm->crtcs[i], "GAMMA_LUT", 0) < 0)
|
||||
return false;
|
||||
if (add_crtc_property(drm->req, &drm->crtcs[i], "ACTIVE", 0) < 0)
|
||||
return false;
|
||||
drm->crtcs[i].pending.active = 0;
|
||||
|
@ -1225,10 +1232,17 @@ int drm_prepare( struct drm_t *drm, const struct Composite_t *pComposite, const
|
|||
|
||||
if (add_crtc_property(drm->req, drm->crtc, "MODE_ID", drm->pending.mode_id) < 0)
|
||||
return false;
|
||||
if (add_crtc_property(drm->req, drm->crtc, "GAMMA_LUT", drm->pending.gamma_lut_id) < 0)
|
||||
return false;
|
||||
if (add_crtc_property(drm->req, drm->crtc, "ACTIVE", 1) < 0)
|
||||
return false;
|
||||
drm->crtc->pending.active = 1;
|
||||
}
|
||||
else if ( drm->pending.gamma_lut_id != drm->current.gamma_lut_id )
|
||||
{
|
||||
if (add_crtc_property(drm->req, drm->crtc, "GAMMA_LUT", drm->pending.gamma_lut_id) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
drm->flags = flags;
|
||||
|
||||
|
@ -1331,6 +1345,75 @@ bool drm_set_connector( struct drm_t *drm, struct connector *conn )
|
|||
return true;
|
||||
}
|
||||
|
||||
inline float srgb_to_linear( float fVal )
|
||||
{
|
||||
return ( fVal < 0.04045f ) ? fVal / 12.92f : std::pow( ( fVal + 0.055f) / 1.055f, 2.4f );
|
||||
}
|
||||
|
||||
inline float linear_to_srgb( float fVal )
|
||||
{
|
||||
return ( fVal < 0.0031308f ) ? fVal * 12.92f : std::pow( fVal, 1.0f / 2.4f ) * 1.055f - 0.055f;
|
||||
}
|
||||
|
||||
inline int quantize( float fVal, float fMaxVal )
|
||||
{
|
||||
return std::max( 0.f, std::min( fMaxVal, roundf( fVal * fMaxVal ) ) );
|
||||
}
|
||||
|
||||
bool drm_set_color_gains(struct drm_t *drm, float *gains)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
drm->pending.color_gain[i] = gains[i];
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if ( drm->current.color_gain[i] != drm->pending.color_gain[i] )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool drm_update_gamma_lut(struct drm_t *drm)
|
||||
{
|
||||
if (drm->pending.color_gain[0] == drm->current.color_gain[0] &&
|
||||
drm->pending.color_gain[1] == drm->current.color_gain[1] &&
|
||||
drm->pending.color_gain[2] == drm->current.color_gain[2])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (drm->pending.color_gain[0] == 1.0f &&
|
||||
drm->pending.color_gain[1] == 1.0f &&
|
||||
drm->pending.color_gain[2] == 1.0f)
|
||||
{
|
||||
drm->pending.gamma_lut_id = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
const int lut_entries = drm->crtc->initial_prop_values["GAMMA_LUT_SIZE"];
|
||||
drm_color_lut *gamma_lut = new drm_color_lut[lut_entries];
|
||||
for ( int i = 0; i < lut_entries; i++ )
|
||||
{
|
||||
float input = float(i) / float(lut_entries - 1);
|
||||
gamma_lut[i].red = quantize( linear_to_srgb( drm->pending.color_gain[0] * srgb_to_linear( input ) ), (float)UINT16_MAX );
|
||||
gamma_lut[i].green = quantize( linear_to_srgb( drm->pending.color_gain[1] * srgb_to_linear( input ) ), (float)UINT16_MAX );
|
||||
gamma_lut[i].blue = quantize( linear_to_srgb( drm->pending.color_gain[2] * srgb_to_linear( input ) ), (float)UINT16_MAX );
|
||||
}
|
||||
|
||||
uint32_t blob_id = 0;
|
||||
if (drmModeCreatePropertyBlob(drm->fd, gamma_lut,
|
||||
lut_entries * sizeof(struct drm_color_lut), &blob_id) != 0) {
|
||||
drm_log.errorf_errno("Unable to create gamma LUT property blob");
|
||||
delete[] gamma_lut;
|
||||
return false;
|
||||
}
|
||||
delete[] gamma_lut;
|
||||
|
||||
drm->pending.gamma_lut_id = blob_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool drm_set_mode( struct drm_t *drm, const drmModeModeInfo *mode )
|
||||
{
|
||||
uint32_t mode_id = 0;
|
||||
|
|
|
@ -95,6 +95,8 @@ struct drm_t {
|
|||
|
||||
struct {
|
||||
uint32_t mode_id;
|
||||
uint32_t gamma_lut_id;
|
||||
float color_gain[3] = { 1.0f, 1.0f, 1.0f };
|
||||
} current, pending;
|
||||
|
||||
/* FBs in the atomic request, but not yet submitted to KMS */
|
||||
|
@ -152,5 +154,7 @@ bool drm_set_connector( struct drm_t *drm, struct connector *conn );
|
|||
bool drm_set_mode( struct drm_t *drm, const drmModeModeInfo *mode );
|
||||
bool drm_set_refresh( struct drm_t *drm, int refresh );
|
||||
bool drm_set_resolution( struct drm_t *drm, int width, int height );
|
||||
bool drm_set_color_gains(struct drm_t *drm, float *gains);
|
||||
bool drm_update_gamma_lut(struct drm_t *drm);
|
||||
|
||||
char *find_drm_node_by_devid(dev_t devid);
|
||||
|
|
|
@ -3305,6 +3305,14 @@ handle_client_message(xwayland_ctx_t *ctx, XClientMessageEvent *ev)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T, typename J>
|
||||
T bit_cast(const J& src) {
|
||||
T dst;
|
||||
memcpy(&dst, &src, sizeof(T));
|
||||
return dst;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev)
|
||||
{
|
||||
|
@ -3559,6 +3567,21 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev)
|
|||
if ( g_fsrUpscale )
|
||||
hasRepaint = true;
|
||||
}
|
||||
if ( ev->atom == ctx->atoms.gamescopeColorLinearGain )
|
||||
{
|
||||
std::vector< uint32_t > user_gains;
|
||||
bool bHasColor = get_prop( ctx, ctx->root, ctx->atoms.gamescopeColorLinearGain, user_gains );
|
||||
|
||||
float gains[3] = { 1.0f, 0.0f, 1.0f };
|
||||
if ( bHasColor && user_gains.size() == 3 )
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
gains[i] = bit_cast<float>( user_gains[i] );
|
||||
}
|
||||
|
||||
if ( drm_set_color_gains( &g_DRM, gains ) )
|
||||
hasRepaint = true;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -4405,6 +4428,8 @@ void init_xwayland_ctx(gamescope_xwayland_server_t *xwayland_server)
|
|||
ctx->atoms.gamescopeScalingFilter = XInternAtom( ctx->dpy, "GAMESCOPE_SCALING_FILTER", false );
|
||||
ctx->atoms.gamescopeFSRSharpness = XInternAtom( ctx->dpy, "GAMESCOPE_FSR_SHARPNESS", false );
|
||||
|
||||
ctx->atoms.gamescopeColorLinearGain = XInternAtom( ctx->dpy, "GAMESCOPE_COLOR_LINEARGAIN", false );
|
||||
|
||||
ctx->root_width = DisplayWidth(ctx->dpy, ctx->scr);
|
||||
ctx->root_height = DisplayHeight(ctx->dpy, ctx->scr);
|
||||
|
||||
|
|
|
@ -115,5 +115,7 @@ struct xwayland_ctx_t
|
|||
|
||||
Atom gamescopeScalingFilter;
|
||||
Atom gamescopeFSRSharpness;
|
||||
|
||||
Atom gamescopeColorLinearGain;
|
||||
} atoms;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue