mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-07-31 04:28:56 +00:00
Get the correct HDR values under Gamescope
This also switches log messages in SDL_x11modes.c to use SDL_Log()
This commit is contained in:
@@ -1455,6 +1455,11 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
|
||||
for (i = 0; i < _this->num_displays; ++i) {
|
||||
SDL_SendDisplayEvent(_this->displays[i], SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED, 0, 0);
|
||||
}
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
|
||||
} else if (SDL_strcmp(name_of_atom, "GAMESCOPE_DISPLAY_IS_EXTERNAL") == 0 ||
|
||||
SDL_strcmp(name_of_atom, "GAMESCOPE_SDR_ON_HDR_CONTENT_BRIGHTNESS") == 0) {
|
||||
X11_CheckDisplaysMoved(_this, display);
|
||||
#endif
|
||||
}
|
||||
X11_XFree(name_of_atom);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "SDL_x11settings.h"
|
||||
#include "edid.h"
|
||||
#include "../../events/SDL_displayevents_c.h"
|
||||
#include "../SDL_pixels_c.h"
|
||||
|
||||
// #define X11MODES_DEBUG
|
||||
|
||||
@@ -344,14 +345,14 @@ static bool CheckXRandR(Display *display, int *major, int *minor)
|
||||
#ifdef XRANDR_DISABLED_BY_DEFAULT
|
||||
if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XRANDR, false)) {
|
||||
#ifdef X11MODES_DEBUG
|
||||
printf("XRandR disabled by default due to window manager issues\n");
|
||||
SDL_Log("XRandR disabled by default due to window manager issues\n");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XRANDR, true)) {
|
||||
#ifdef X11MODES_DEBUG
|
||||
printf("XRandR disabled due to hint\n");
|
||||
SDL_Log("XRandR disabled due to hint\n");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
@@ -359,7 +360,7 @@ static bool CheckXRandR(Display *display, int *major, int *minor)
|
||||
|
||||
if (!SDL_X11_HAVE_XRANDR) {
|
||||
#ifdef X11MODES_DEBUG
|
||||
printf("XRandR support not available\n");
|
||||
SDL_Log("XRandR support not available\n");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
@@ -369,13 +370,13 @@ static bool CheckXRandR(Display *display, int *major, int *minor)
|
||||
*minor = 3; // we want 1.3
|
||||
if (!X11_XRRQueryVersion(display, major, minor)) {
|
||||
#ifdef X11MODES_DEBUG
|
||||
printf("XRandR not active on the display\n");
|
||||
SDL_Log("XRandR not active on the display\n");
|
||||
#endif
|
||||
*major = *minor = 0;
|
||||
return false;
|
||||
}
|
||||
#ifdef X11MODES_DEBUG
|
||||
printf("XRandR available at version %d.%d!\n", *major, *minor);
|
||||
SDL_Log("XRandR available at version %d.%d!\n", *major, *minor);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
@@ -440,8 +441,8 @@ static bool SetXRandRModeInfo(Display *display, XRRScreenResources *res, RRCrtc
|
||||
CalculateXRandRRefreshRate(info, &mode->refresh_rate_numerator, &mode->refresh_rate_denominator);
|
||||
mode->internal->xrandr_mode = modeID;
|
||||
#ifdef X11MODES_DEBUG
|
||||
printf("XRandR mode %d: %dx%d@%d/%dHz\n", (int)modeID,
|
||||
mode->screen_w, mode->screen_h, mode->refresh_rate_numerator, mode->refresh_rate_denominator);
|
||||
SDL_Log("XRandR mode %d: %dx%d@%d/%dHz\n", (int)modeID,
|
||||
mode->screen_w, mode->screen_h, mode->refresh_rate_numerator, mode->refresh_rate_denominator);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
@@ -449,7 +450,90 @@ static bool SetXRandRModeInfo(Display *display, XRRScreenResources *res, RRCrtc
|
||||
return false;
|
||||
}
|
||||
|
||||
static MonitorInfo *GetMonitorInfo(Display *dpy, int screen, RROutput output, const char *name)
|
||||
static bool GetRootWindowCardinalProperty(Display *dpy, int screen, const char *name, Uint32 *value)
|
||||
{
|
||||
Atom atom = X11_XInternAtom(dpy, name, False);
|
||||
if (atom == None) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int real_format;
|
||||
Atom real_type;
|
||||
unsigned long items_read = 0, items_left = 0;
|
||||
unsigned char *propdata = NULL;
|
||||
int status;
|
||||
bool result = false;
|
||||
|
||||
status = X11_XGetWindowProperty(dpy, RootWindow(dpy, screen),
|
||||
atom, 0L, 1024L, False, AnyPropertyType,
|
||||
&real_type, &real_format, &items_read, &items_left, &propdata);
|
||||
if (status == Success && propdata) {
|
||||
if (real_type == XA_CARDINAL && items_read > 0) {
|
||||
switch (real_format) {
|
||||
case 8:
|
||||
*value = propdata[0];
|
||||
break;
|
||||
case 16:
|
||||
*value = propdata[0] | (propdata[1] << 8);
|
||||
break;
|
||||
default:
|
||||
*value = propdata[0] | (propdata[1] << 8) | (propdata[2] << 16) | (propdata[3] << 24);
|
||||
break;
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
X11_XFree(propdata);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool GetRootWindowBoolProperty(Display *dpy, int screen, const char *name, bool *value)
|
||||
{
|
||||
Uint32 v;
|
||||
if (GetRootWindowCardinalProperty(dpy, screen, name, &v)) {
|
||||
*value = (v != 0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool GetRootWindowFloatProperty(Display *dpy, int screen, const char *name, float *value)
|
||||
{
|
||||
Uint32 v;
|
||||
if (GetRootWindowCardinalProperty(dpy, screen, name, &v)) {
|
||||
SDL_memcpy(value, &v, sizeof(v));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static float GetGamescopeSDRWhiteLevel(Display *dpy, int screen)
|
||||
{
|
||||
// These properties are currently only available on X display :0
|
||||
// If this changes, please remove the XCloseDisplay() call below.
|
||||
dpy = X11_XOpenDisplay(":0");
|
||||
screen = 0;
|
||||
if (!dpy) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float SDR_white_level = 0.0f;
|
||||
bool external_display = false;
|
||||
if (GetRootWindowBoolProperty(dpy, screen, "GAMESCOPE_DISPLAY_IS_EXTERNAL", &external_display) &&
|
||||
external_display) {
|
||||
GetRootWindowFloatProperty(dpy, screen, "GAMESCOPE_SDR_ON_HDR_CONTENT_BRIGHTNESS", &SDR_white_level);
|
||||
} else {
|
||||
// We're using the Steam Deck internal display, which has an SDR white level of 500 nits
|
||||
SDR_white_level = 500.0f;
|
||||
}
|
||||
|
||||
// Closing the display opened above
|
||||
X11_XCloseDisplay(dpy);
|
||||
|
||||
return SDR_white_level;
|
||||
}
|
||||
|
||||
static MonitorInfo *GetMonitorInfo(Display *dpy, int screen, RROutput output, bool *gamescope)
|
||||
{
|
||||
MonitorInfo *info = NULL;
|
||||
int real_format;
|
||||
@@ -458,6 +542,7 @@ static MonitorInfo *GetMonitorInfo(Display *dpy, int screen, RROutput output, co
|
||||
unsigned char *propdata = NULL;
|
||||
int status;
|
||||
|
||||
*gamescope = false;
|
||||
|
||||
if (!info) {
|
||||
Atom GAMESCOPE_DISPLAY_EDID_PATH = X11_XInternAtom(dpy, "GAMESCOPE_DISPLAY_EDID_PATH", False);
|
||||
@@ -470,6 +555,9 @@ static MonitorInfo *GetMonitorInfo(Display *dpy, int screen, RROutput output, co
|
||||
void *data = SDL_LoadFile((char *)propdata, &size);
|
||||
if (data) {
|
||||
info = decode_edid((uchar *)data, size);
|
||||
if (info) {
|
||||
*gamescope = true;
|
||||
}
|
||||
SDL_free(data);
|
||||
}
|
||||
}
|
||||
@@ -503,7 +591,6 @@ static MonitorInfo *GetMonitorInfo(Display *dpy, int screen, RROutput output, co
|
||||
|
||||
#ifdef X11MODES_DEBUG
|
||||
if (info) {
|
||||
SDL_Log("Found EDID data for %s", name);
|
||||
dump_monitor_info(info);
|
||||
}
|
||||
#endif
|
||||
@@ -523,7 +610,7 @@ static void SetXRandRDisplayName(MonitorInfo *info, char *name, size_t namelen,
|
||||
}
|
||||
|
||||
#ifdef X11MODES_DEBUG
|
||||
printf("Display name: %s\n", name);
|
||||
SDL_Log("Display name: %s\n", name);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -621,7 +708,8 @@ static bool X11_FillXRandRDisplayInfo(SDL_VideoDevice *_this, Display *dpy, int
|
||||
displaydata->xrandr_output = outputid;
|
||||
SDL_strlcpy(displaydata->connector_name, display_name, sizeof(displaydata->connector_name));
|
||||
|
||||
MonitorInfo *info = GetMonitorInfo(dpy, screen, outputid, display_name);
|
||||
bool gamescope = false;
|
||||
MonitorInfo *info = GetMonitorInfo(dpy, screen, outputid, &gamescope);
|
||||
|
||||
SetXRandRModeInfo(dpy, res, output_crtc, modeID, &mode);
|
||||
SetXRandRDisplayName(info, display_name, display_name_size, display_mm_width, display_mm_height);
|
||||
@@ -635,32 +723,20 @@ static bool X11_FillXRandRDisplayInfo(SDL_VideoDevice *_this, Display *dpy, int
|
||||
display->internal = displaydata;
|
||||
|
||||
if (info) {
|
||||
/* ITU-R BT.2408-7 (Sept 2023) has the reference PQ white level at 203 nits,
|
||||
* while older Dolby documentation claims a reference level of 100 nits.
|
||||
*
|
||||
* Use 203 nits for now.
|
||||
*/
|
||||
float SDR_white_level = 203.0f;
|
||||
|
||||
bool steam_deck = SDL_GetHintBoolean("SteamDeck", false);
|
||||
if (steam_deck) {
|
||||
// The SDR white level is dynamic, but typically is 400 nits
|
||||
// We'll need some other way to get this for external displays
|
||||
SDR_white_level = 400.0f;
|
||||
float SDR_white_level;
|
||||
if (gamescope) {
|
||||
SDR_white_level = GetGamescopeSDRWhiteLevel(dpy, screen);
|
||||
} else {
|
||||
// Support for HDR on X11 seems spotty, let's disable this for now
|
||||
SDR_white_level = 0.0f;
|
||||
}
|
||||
|
||||
// Allow a hint override for the SDR white level
|
||||
const char *hint = SDL_GetHint("SDL_X11_SDR_WHITE_LEVEL");
|
||||
if (hint && *hint) {
|
||||
SDR_white_level = (float)SDL_atof(hint);
|
||||
}
|
||||
|
||||
if (info->max_luminance > 0.0 && SDR_white_level > 0.0f) {
|
||||
#ifdef X11MODES_DEBUG
|
||||
SDL_Log("HDR values: %f / %f", SDR_white_level, info->max_luminance);
|
||||
#endif
|
||||
if (SDR_white_level > 0.0f && info->max_luminance > SDR_white_level) {
|
||||
display->HDR.HDR_headroom = (float)info->max_luminance / SDR_white_level;
|
||||
display->HDR.SDR_white_level = SDR_white_level / 80.0f;
|
||||
display->HDR.SDR_white_level = SDR_white_level / SCRGB_NITS;
|
||||
}
|
||||
|
||||
SDL_free(info);
|
||||
@@ -716,8 +792,11 @@ static bool X11_UpdateXRandRDisplay(SDL_VideoDevice *_this, Display *dpy, int sc
|
||||
// update scale
|
||||
SDL_SetDisplayContentScale(existing_display, display.content_scale);
|
||||
|
||||
// update HDR properties
|
||||
SDL_SetDisplayHDRProperties(existing_display, &display.HDR);
|
||||
|
||||
// SDL_DisplayData is updated piece-meal above, free our local copy of this data
|
||||
SDL_free( display.internal );
|
||||
SDL_free(display.internal);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -734,7 +813,7 @@ static XRRScreenResources *X11_GetScreenResources(Display *dpy, int screen)
|
||||
return res;
|
||||
}
|
||||
|
||||
static void X11_CheckDisplaysMoved(SDL_VideoDevice *_this, Display *dpy)
|
||||
void X11_CheckDisplaysMoved(SDL_VideoDevice *_this, Display *dpy)
|
||||
{
|
||||
const int screencount = ScreenCount(dpy);
|
||||
|
||||
@@ -816,7 +895,7 @@ static void X11_HandleXRandROutputChange(SDL_VideoDevice *_this, const XRROutput
|
||||
int i;
|
||||
|
||||
#if 0
|
||||
printf("XRROutputChangeNotifyEvent! [output=%u, crtc=%u, mode=%u, rotation=%u, connection=%u]\n", (unsigned int) ev->output, (unsigned int) ev->crtc, (unsigned int) ev->mode, (unsigned int) ev->rotation, (unsigned int) ev->connection);
|
||||
SDL_Log("XRROutputChangeNotifyEvent! [output=%u, crtc=%u, mode=%u, rotation=%u, connection=%u]\n", (unsigned int) ev->output, (unsigned int) ev->crtc, (unsigned int) ev->mode, (unsigned int) ev->rotation, (unsigned int) ev->connection);
|
||||
#endif
|
||||
|
||||
// XWayland doesn't always send output disconnected events
|
||||
@@ -1125,8 +1204,8 @@ bool X11_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, S
|
||||
|
||||
if (crtc->mode == modedata->xrandr_mode) {
|
||||
#ifdef X11MODES_DEBUG
|
||||
printf("already in desired mode 0x%lx (%ux%u), nothing to do\n",
|
||||
crtc->mode, crtc->width, crtc->height);
|
||||
SDL_Log("already in desired mode 0x%lx (%ux%u), nothing to do\n",
|
||||
crtc->mode, crtc->width, crtc->height);
|
||||
#endif
|
||||
status = Success;
|
||||
goto freeInfo;
|
||||
|
||||
@@ -68,6 +68,7 @@ extern float X11_GetGlobalContentScale(Display *display, XSettingsClient *client
|
||||
extern float X11_GetGlobalContentScaleForDevice(SDL_VideoDevice *_this);
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
|
||||
extern void X11_CheckDisplaysMoved(SDL_VideoDevice *_this, Display *dpy);
|
||||
extern void X11_HandleXRandREvent(SDL_VideoDevice *_this, const XEvent *xevent);
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user