protocol: Add display_info to gamescope-control protocol
This commit is contained in:
parent
6f7e0c916c
commit
b7f319aea1
7 changed files with 185 additions and 11 deletions
|
@ -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>
|
||||
|
|
67
src/drm.cpp
67
src/drm.cpp
|
@ -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>{};
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue