Skip to content

Commit

Permalink
protocol: Add display_info to gamescope-control protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
misyltoad committed Oct 6, 2023
1 parent 6f7e0c9 commit b7f319a
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 11 deletions.
41 changes: 33 additions & 8 deletions protocol/gamescope-control.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand All @@ -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 changes: 66 additions & 1 deletion src/drm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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 );
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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>{};
}
5 changes: 5 additions & 0 deletions src/drm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down
59 changes: 59 additions & 0 deletions src/steamcompmgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/steamcompmgr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
18 changes: 16 additions & 2 deletions src/wlserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ extern "C" {

static LogScope wl_log("wlserver");

static struct wlserver_t wlserver = {
struct wlserver_t wlserver = {
.touch_down_ids = {}
};

Expand Down Expand Up @@ -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 )
Expand All @@ -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 );
}

Expand Down
4 changes: 4 additions & 0 deletions src/wlserver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit b7f319a

Please sign in to comment.