protocol: Add display_info to gamescope-control protocol

This commit is contained in:
Joshua Ashton 2023-10-06 00:55:50 +01:00 committed by Joshie
parent 6f7e0c916c
commit b7f319aea1
7 changed files with 185 additions and 11 deletions

View file

@ -29,7 +29,7 @@
it.
</description>
<interface name="gamescope_control" version="1">
<interface name="gamescope_control" version="2">
<request name="destroy" type="destructor"></request>
<enum name="feature">
@ -38,15 +38,40 @@
</description>
<entry name="done" value="0" summary="sent at the end of the feature list"/>
<entry name="reshade_shaders" value="1"/>
<entry name="display_info" value="2"/>
</enum>
<event name="feature_support">
<description summary="feature supported">
Says whether a feature is supported and the version.
</description>
<arg name="feature" type="uint" summary="one of the enum features"/>
<arg name="version" type="uint" summary="feature version"/>
<arg name="flags" type="uint" summary="feature flags (none currently)"/>
</event>
<description summary="feature supported">
Says whether a feature is supported and the version.
</description>
<arg name="feature" type="uint" summary="one of the enum features"/>
<arg name="version" type="uint" summary="feature version"/>
<arg name="flags" type="uint" summary="feature flags"/>
</event>
<enum name="display_flag" bitfield="true" since="2">
<entry name="internal_display" value="0x1"/>
<entry name="supports_hdr" value="0x2"/>
<entry name="supports_vrr" value="0x4"/>
</enum>
<event name="active_display_info" since="2">
<arg name="connector_name" type="string"/>
<arg name="display_make" type="string"/>
<arg name="display_model" type="string"/>
<arg name="display_flags" type="uint" enum="display_flag" summary="combination of 'display_flag' values"/>
<arg name="valid_refresh_rates" type="array" summary="valid refresh rates for the display for the purposes of combined fps limiting + refresh. empty if fixed at a mode. 32-bit unsigned integers."/>
</event>
<enum name="target_refresh_cycle_flag" bitfield="true" since="2">
<entry name="internal_display" value="0x1" summary="Whether we should change the target refresh cycle under external/internal display"/>
<entry name="allow_refresh_switching" value="0x2" summary="Whether or not to allow refresh rate switching"/>
</enum>
<request name="set_app_target_refresh_cycle" since="2">
<arg name="fps" type="uint" summary="application's target refresh cycle in frames-per-second. 0 = disabled/native"></arg>
<arg name="flags" type="uint" enum="target_refresh_cycle_flag" summary="combination of 'target_refresh_cycle_flag' values"/>
</request>
</interface>
</protocol>

View file

@ -38,6 +38,8 @@ extern "C" {
#include "libdisplay-info/cta.h"
}
#include "gamescope-control-protocol.h"
struct drm_t g_DRM = {};
uint32_t g_nDRMFormat = DRM_FORMAT_INVALID;
@ -68,6 +70,55 @@ drm_screen_type drm_get_connector_type(drmModeConnector *connector);
static void drm_unset_mode( struct drm_t *drm );
static void drm_unset_connector( struct drm_t *drm );
static uint32_t steam_deck_display_rates[] =
{
40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60,
};
static uint32_t get_conn_display_info_flags(struct drm_t *drm, struct connector *connector)
{
if (!connector)
return 0;
uint32_t flags = 0;
if ( drm_get_connector_type(connector->connector) == DRM_SCREEN_TYPE_INTERNAL )
flags |= GAMESCOPE_CONTROL_DISPLAY_FLAG_INTERNAL_DISPLAY;
if ( connector->vrr_capable )
flags |= GAMESCOPE_CONTROL_DISPLAY_FLAG_SUPPORTS_VRR;
if ( connector->metadata.supportsST2084 )
flags |= GAMESCOPE_CONTROL_DISPLAY_FLAG_SUPPORTS_HDR;
return flags;
}
static void update_connector_display_info_wl(struct drm_t *drm)
{
if ( !drm->connector )
return;
auto& conn = drm->connector;
wlserver_lock();
for ( const auto &control : wlserver.gamescope_controls )
{
uint32_t flags = get_conn_display_info_flags( drm, drm->connector );
struct wl_array display_rates;
wl_array_init(&display_rates);
if ( conn->valid_display_rates.size() )
{
size_t size = conn->valid_display_rates.size() * sizeof(uint32_t);
uint32_t *ptr = (uint32_t *)wl_array_add( &display_rates, size );
memcpy( ptr, conn->valid_display_rates.data(), size );
}
gamescope_control_send_active_display_info( control, drm->connector->name, drm->connector->make, drm->connector->model, flags, &display_rates );
wl_array_release(&display_rates);
}
wlserver_unlock();
}
inline uint64_t drm_calc_s31_32(float val)
{
// S31.32 sign-magnitude
@ -83,7 +134,7 @@ inline uint64_t drm_calc_s31_32(float val)
uint64_t sign_part : 1;
} s31_32_bits;
uint64_t s31_32;
} color;4
} color;
color.s31_32_bits.sign_part = val < 0 ? 1 : 0;
color.s31_32_bits.integral = uint64_t( integral );
@ -824,6 +875,9 @@ static void parse_edid( drm_t *drm, struct connector *conn)
(strcmp(conn->make_pnp, "VLV") == 0 && strcmp(conn->model, "ANX7530 U") == 0) ||
(strcmp(conn->make_pnp, "VLV") == 0 && strcmp(conn->model, "Jupiter") == 0);
if ( conn->is_steam_deck_display )
conn->valid_display_rates = std::span(steam_deck_display_rates);
drm_hdr_parse_edid(drm, conn, edid);
di_info_destroy(info);
@ -1226,6 +1280,8 @@ static bool setup_best_connector(struct drm_t *drm, bool force, bool initial)
if (!initial)
create_patched_edid(best->edid_data.data(), best->edid_data.size(), drm, best);
update_connector_display_info_wl( drm );
return true;
}
@ -3112,3 +3168,12 @@ void drm_get_native_colorimetry( struct drm_t *drm,
*outputEncodingEOTF = EOTF_Gamma22;
}
}
std::span<uint32_t> drm_get_valid_refresh_rates( struct drm_t *drm )
{
if (drm && drm->connector)
return drm->connector->valid_display_rates;
return std::span<uint32_t>{};
}

View file

@ -8,6 +8,8 @@
#include <drm_fourcc.h>
#include <drm_mode.h>
#include <span>
#include "color_helpers.h"
// Josh: Okay whatever, this header isn't
@ -146,6 +148,7 @@ struct connector {
char *make;
char *model;
bool is_steam_deck_display;
std::span<uint32_t> valid_display_rates{};
int target_refresh;
bool vrr_capable;
@ -336,6 +339,8 @@ void drm_get_native_colorimetry( struct drm_t *drm,
displaycolorimetry_t *displayColorimetry, EOTF *displayEOTF,
displaycolorimetry_t *outputEncodingColorimetry, EOTF *outputEncodingEOTF );
std::span<uint32_t> drm_get_valid_refresh_rates( struct drm_t *drm );
extern bool g_bSupportsAsyncFlips;
/* from CTA-861-G */

View file

@ -752,6 +752,63 @@ static int g_nDynamicRefreshRate[DRM_SCREEN_TYPE_COUNT] = { 0, 0 };
// Delay to stop modes flickering back and forth.
static const uint64_t g_uDynamicRefreshDelay = 600'000'000; // 600ms
static int g_nCombinedAppRefreshCycleOverride[DRM_SCREEN_TYPE_COUNT] = { 0, 0 };
static void update_app_target_refresh_cycle()
{
if ( BIsNested() )
{
g_nDynamicRefreshRate[ DRM_SCREEN_TYPE_INTERNAL ] = 0;
g_nSteamCompMgrTargetFPS = g_nCombinedAppRefreshCycleOverride[ DRM_SCREEN_TYPE_INTERNAL ];
return;
}
static drm_screen_type last_type;
static int last_target_fps;
static bool first = true;
drm_screen_type type = drm_get_screen_type( &g_DRM );
int target_fps = g_nCombinedAppRefreshCycleOverride[type];
if ( !first && type == last_type && last_target_fps == target_fps )
{
return;
}
last_type = type;
last_target_fps = target_fps;
first = false;
if ( !target_fps )
{
g_nDynamicRefreshRate[ type ] = 0;
g_nSteamCompMgrTargetFPS = 0;
return;
}
auto rates = drm_get_valid_refresh_rates( &g_DRM );
g_nDynamicRefreshRate[ type ] = 0;
g_nSteamCompMgrTargetFPS = target_fps;
// Find highest mode to do refresh doubling with.
for ( auto rate = rates.rbegin(); rate != rates.rend(); rate++ )
{
if (*rate % target_fps == 0)
{
g_nDynamicRefreshRate[ type ] = *rate;
g_nSteamCompMgrTargetFPS = target_fps;
return;
}
}
}
void steamcompmgr_set_app_refresh_cycle_override( drm_screen_type type, int override_fps )
{
g_nCombinedAppRefreshCycleOverride[ type ] = override_fps;
update_app_target_refresh_cycle();
}
static int g_nRuntimeInfoFd = -1;
bool g_bFSRActive = false;
@ -2410,6 +2467,8 @@ paint_all(bool async)
pw_buffer = dequeue_pipewire_buffer();
#endif
update_app_target_refresh_cycle();
int nDynamicRefresh = g_nDynamicRefreshRate[drm_get_screen_type( &g_DRM )];
int nTargetRefresh = nDynamicRefresh && steamcompmgr_window_should_limit_fps( global_focus.focusWindow )// && !global_focus.overlayWindow

View file

@ -148,3 +148,5 @@ MouseCursor *steamcompmgr_get_current_cursor();
MouseCursor *steamcompmgr_get_server_cursor(uint32_t serverId);
extern int g_nAsyncFlipsEnabled;
extern void steamcompmgr_set_app_refresh_cycle_override( drm_screen_type type, int override_fps );

View file

@ -65,7 +65,7 @@ extern "C" {
static LogScope wl_log("wlserver");
static struct wlserver_t wlserver = {
struct wlserver_t wlserver = {
.touch_down_ids = {}
};
@ -866,13 +866,24 @@ static void create_gamescope_pipewire( void )
//
static void gamescope_control_set_app_target_refresh_cycle( struct wl_client *client, struct wl_resource *resource, uint32_t fps, uint32_t flags )
{
auto display_type = DRM_SCREEN_TYPE_EXTERNAL;
if ( flags & GAMESCOPE_CONTROL_TARGET_REFRESH_CYCLE_FLAG_INTERNAL_DISPLAY )
display_type = DRM_SCREEN_TYPE_INTERNAL;
steamcompmgr_set_app_refresh_cycle_override( display_type, fps );
}
static void gamescope_control_handle_destroy( struct wl_client *client, struct wl_resource *resource )
{
std::erase_if(wlserver.gamescope_controls, [=](auto control) { return control == resource; });
wl_resource_destroy( resource );
}
static const struct gamescope_control_interface gamescope_control_impl = {
.destroy = gamescope_control_handle_destroy,
.set_app_target_refresh_cycle = gamescope_control_set_app_target_refresh_cycle,
};
static void gamescope_control_bind( struct wl_client *client, void *data, uint32_t version, uint32_t id )
@ -882,12 +893,15 @@ static void gamescope_control_bind( struct wl_client *client, void *data, uint32
// Send feature support
gamescope_control_send_feature_support( resource, GAMESCOPE_CONTROL_FEATURE_RESHADE_SHADERS, 1, 0 );
gamescope_control_send_feature_support( resource, GAMESCOPE_CONTROL_FEATURE_DISPLAY_INFO, 1, 0 );
gamescope_control_send_feature_support( resource, GAMESCOPE_CONTROL_FEATURE_DONE, 0, 0 );
wlserver.gamescope_controls.push_back(resource);
}
static void create_gamescope_control( void )
{
uint32_t version = 1;
uint32_t version = 2;
wl_global_create( wlserver.display, &gamescope_control_interface, version, NULL, gamescope_control_bind );
}

View file

@ -136,8 +136,12 @@ struct wlserver_t {
std::atomic<bool> xdg_dirty;
std::mutex xdg_commit_lock;
std::vector<ResListEntry_t> xdg_commit_queue;
std::vector<wl_resource*> gamescope_controls;
};
extern struct wlserver_t wlserver;
std::vector<ResListEntry_t> wlserver_xdg_commit_queue();
struct wlserver_keyboard {