Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Support for multiple virtual connectors #1486

Draft
wants to merge 32 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3a3f910
all: Support for multiple virtual connectors
misyltoad Aug 22, 2024
953cd7f
main: Add --virtual-connector-strategy
misyltoad Aug 22, 2024
d80cf55
TODO Figure out why dirty focuses doesn't work there
misyltoad Aug 22, 2024
19f7d95
OpeNVRBackend: Add vr_nudge_to_visible_per_connector
misyltoad Aug 22, 2024
a4a4948
OpenVRBackend: Add logging for overlay visible count
misyltoad Aug 23, 2024
f03168c
OpenVRBackend: Consider visible when an appid's scene app is visible
misyltoad Aug 23, 2024
de62f4f
OpenVRBackend: Add vr-app-overlay-key
misyltoad Aug 23, 2024
f06105f
steamcompmgr: Clean up g_VirtualConnectorFocuses before closing backe…
misyltoad Aug 23, 2024
56a854f
OpenVRBackend: Add logging for creating a new dashboard overlay
misyltoad Aug 23, 2024
efd874b
OpenVRBackend: Fix crash with SteamVR input thread
misyltoad Aug 23, 2024
ded1055
SDLBackend: Fix with virtual connector backend
misyltoad Aug 26, 2024
a99c466
steamcompmgr: Fix Steam intergration with multiple virtual connectors
misyltoad Aug 27, 2024
01b18bf
LibInputHandler: add, hook up to vr-session-manager
misyltoad Aug 28, 2024
7faf1df
LibInputHandler: Add support for scroll wheel
misyltoad Aug 28, 2024
07b2788
OpenVRBackend: Support for physical mouse input controlling cursor
misyltoad Aug 28, 2024
c26c237
OpenVRBackend: Only override cursor if not in relative mode.
misyltoad Aug 28, 2024
c2b6432
Fix build
misyltoad Aug 29, 2024
b1f50ed
Fix build 2
misyltoad Aug 29, 2024
35e3125
Fix build 3
misyltoad Aug 29, 2024
81cf4ef
DRMBackend: Fix VRR capable check
misyltoad Aug 29, 2024
6af0dd4
OpenVRBackend: Fix OverlayClosed when not in steam mode
misyltoad Sep 11, 2024
4c45f23
messagey: Add GAMESCOPE_ZENITY_DISABLE
misyltoad Sep 11, 2024
075bc42
OpenVRBackend: Fix for dual/double frame jitter
misyltoad Nov 21, 2024
0851ed6
OpenVRBackend: Fix FPS limit not updating
misyltoad Nov 21, 2024
970f911
GAMESCOPE_MANGOAPP_SOCKET_DISABLE added
Sep 12, 2024
ad26d7f
rendervulkan: Fix a CVulkanCmdBuffer leak that could result in screen…
lostgoat Oct 29, 2024
611a476
steamcompmgr: Fix crash when using magnifier and game recording
misyltoad Sep 19, 2024
6a60ac9
pipewire: cmpxchng in_buffer + out_buffer to nullptr if they equal to…
misyltoad Aug 29, 2024
0739d4a
OpenVRBackend: Disable explicit sync for now
misyltoad Nov 21, 2024
ee66a97
Revert "steamcompmgr: Fix crash when using magnifier and game recording"
misyltoad Nov 21, 2024
15a198b
backend: Hack
misyltoad Nov 21, 2024
c96d36c
OpenVRBackend: Add mutex around fb id tracking
misyltoad Nov 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 133 additions & 100 deletions src/Backends/DRMBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,95 @@ gamescope::ConVar<bool> cv_drm_debug_disable_in_fence_fd( "drm_debug_disable_in_
// since we moved to the upstream properites and a bunch of work happened.
gamescope::ConVar<bool> cv_drm_hack_nv12_color_mgmt_fix( "drm_hack_nv12_color_mgmt_fix", true, "If using NV12, disable explicit degamma + shaper + 3D LUT" );

int HackyDRMPresent( const FrameInfo_t *pFrameInfo, bool bAsync );

struct saved_mode {
int width;
int height;
int refresh;
};

namespace gamescope
{
class CDRMPlane;
class CDRMCRTC;
class CDRMConnector;
}

struct drm_t {
bool bUseLiftoff;

int fd = -1;

int preferred_width, preferred_height, preferred_refresh;

uint64_t cursor_width, cursor_height;
bool allow_modifiers;
struct wlr_drm_format_set formats;

std::vector< std::unique_ptr< gamescope::CDRMPlane > > planes;
std::vector< std::unique_ptr< gamescope::CDRMCRTC > > crtcs;
std::unordered_map< uint32_t, gamescope::CDRMConnector > connectors;

gamescope::CDRMPlane *pPrimaryPlane;
gamescope::CDRMCRTC *pCRTC;
gamescope::CDRMConnector *pConnector;

struct wlr_drm_format_set primary_formats;

drmModeAtomicReq *req;
uint32_t flags;

struct liftoff_device *lo_device;
struct liftoff_output *lo_output;
struct liftoff_layer *lo_layers[ k_nMaxLayers ];

std::shared_ptr<gamescope::BackendBlob> sdr_static_metadata;

struct drm_state_t {
std::shared_ptr<gamescope::BackendBlob> mode_id;
uint32_t color_mgmt_serial;
std::shared_ptr<gamescope::BackendBlob> lut3d_id[ EOTF_Count ];
std::shared_ptr<gamescope::BackendBlob> shaperlut_id[ EOTF_Count ];
amdgpu_transfer_function output_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
} current, pending;

// FBs in the atomic request, but not yet submitted to KMS
// Accessed only on req thread
std::vector<gamescope::Rc<gamescope::IBackendFb>> m_FbIdsInRequest;

// FBs currently queued to go on screen.
// May be accessed by page flip handler thread and req thread, thus mutex.
std::mutex m_QueuedFbIdsMutex;
std::vector<gamescope::Rc<gamescope::IBackendFb>> m_QueuedFbIds;
// FBs currently on screen.
// Accessed only on page flip handler thread.
std::mutex m_mutVisibleFbIds;
std::vector<gamescope::Rc<gamescope::IBackendFb>> m_VisibleFbIds;

std::atomic < uint32_t > uPendingFlipCount = { 0 };

std::atomic < bool > paused = { false };
std::atomic < int > out_of_date = { false };
std::atomic < bool > needs_modeset = { false };

std::unordered_map< std::string, int > connector_priorities;

char *device_name = nullptr;
};

void drm_drop_fbid( struct drm_t *drm, uint32_t fbid );
bool drm_set_mode( struct drm_t *drm, const drmModeModeInfo *mode );


using namespace std::literals;

struct drm_t g_DRM = {};

namespace gamescope
{
class CDRMBackend;

std::tuple<int32_t, int32_t, int32_t> GetKernelVersion()
{
utsname name;
Expand Down Expand Up @@ -267,10 +354,10 @@ namespace gamescope
CRTCProperties m_Props;
};

class CDRMConnector final : public IBackendConnector, public CDRMAtomicTypedObject<DRM_MODE_OBJECT_CONNECTOR>
class CDRMConnector final : public CBaseBackendConnector, public CDRMAtomicTypedObject<DRM_MODE_OBJECT_CONNECTOR>
{
public:
CDRMConnector( drmModeConnector *pConnector );
CDRMConnector( CDRMBackend *pBackend, drmModeConnector *pConnector );

void RefreshState();

Expand Down Expand Up @@ -352,6 +439,14 @@ namespace gamescope

const BackendConnectorHDRInfo &GetHDRInfo() const override { return m_Mutable.HDR; }

virtual bool IsVRRActive() const override
{
if ( !g_DRM.pCRTC || !g_DRM.pCRTC->GetProperties().VRR_ENABLED )
return false;

return !!g_DRM.pCRTC->GetProperties().VRR_ENABLED->GetCurrentValue();
}

virtual std::span<const BackendMode> GetModes() const override { return m_Mutable.BackendModes; }

bool SupportsVRR() const override
Expand Down Expand Up @@ -380,13 +475,17 @@ namespace gamescope
}
}

virtual int Present( const FrameInfo_t *pFrameInfo, bool bAsync ) override;

void UpdateEffectiveOrientation( const drmModeModeInfo *pMode );


private:
void ParseEDID();

static std::optional<BackendConnectorHDRInfo> GetKnownDisplayHDRInfo( GamescopeKnownDisplays eKnownDisplay );

CDRMBackend *m_pBackend = nullptr;
CAutoDeletePtr<drmModeConnector> m_pConnector;

struct MutableConnectorState
Expand Down Expand Up @@ -425,82 +524,6 @@ namespace gamescope
};
}

struct saved_mode {
int width;
int height;
int refresh;
};

struct drm_t {
bool bUseLiftoff;

int fd = -1;

int preferred_width, preferred_height, preferred_refresh;

uint64_t cursor_width, cursor_height;
bool allow_modifiers;
struct wlr_drm_format_set formats;

std::vector< std::unique_ptr< gamescope::CDRMPlane > > planes;
std::vector< std::unique_ptr< gamescope::CDRMCRTC > > crtcs;
std::unordered_map< uint32_t, gamescope::CDRMConnector > connectors;

gamescope::CDRMPlane *pPrimaryPlane;
gamescope::CDRMCRTC *pCRTC;
gamescope::CDRMConnector *pConnector;

struct wlr_drm_format_set primary_formats;

drmModeAtomicReq *req;
uint32_t flags;

struct liftoff_device *lo_device;
struct liftoff_output *lo_output;
struct liftoff_layer *lo_layers[ k_nMaxLayers ];

std::shared_ptr<gamescope::BackendBlob> sdr_static_metadata;

struct drm_state_t {
std::shared_ptr<gamescope::BackendBlob> mode_id;
uint32_t color_mgmt_serial;
std::shared_ptr<gamescope::BackendBlob> lut3d_id[ EOTF_Count ];
std::shared_ptr<gamescope::BackendBlob> shaperlut_id[ EOTF_Count ];
amdgpu_transfer_function output_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
} current, pending;

// FBs in the atomic request, but not yet submitted to KMS
// Accessed only on req thread
std::vector<gamescope::Rc<gamescope::IBackendFb>> m_FbIdsInRequest;

// FBs currently queued to go on screen.
// May be accessed by page flip handler thread and req thread, thus mutex.
std::mutex m_QueuedFbIdsMutex;
std::vector<gamescope::Rc<gamescope::IBackendFb>> m_QueuedFbIds;
// FBs currently on screen.
// Accessed only on page flip handler thread.
std::mutex m_mutVisibleFbIds;
std::vector<gamescope::Rc<gamescope::IBackendFb>> m_VisibleFbIds;

std::atomic < uint32_t > uPendingFlipCount = { 0 };

std::atomic < bool > paused = { false };
std::atomic < int > out_of_date = { false };
std::atomic < bool > needs_modeset = { false };

std::unordered_map< std::string, int > connector_priorities;

char *device_name = nullptr;
};

void drm_drop_fbid( struct drm_t *drm, uint32_t fbid );
bool drm_set_mode( struct drm_t *drm, const drmModeModeInfo *mode );


using namespace std::literals;

struct drm_t g_DRM = {};

uint32_t g_nDRMFormat = DRM_FORMAT_INVALID;
uint32_t g_nDRMFormatOverlay = DRM_FORMAT_INVALID; // for partial composition, we may have more limited formats than base planes + alpha.
bool g_bRotated = false;
Expand Down Expand Up @@ -564,7 +587,7 @@ static constexpr uint32_t s_kSteamDeckOLEDRates[] =
90,
};

static void update_connector_display_info_wl(struct drm_t *drm)
void update_connector_display_info_wl(struct drm_t *drm)
{
wlserver_lock();
for ( const auto &control : wlserver.gamescope_controls )
Expand Down Expand Up @@ -686,7 +709,7 @@ static void page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsi
DRMPresentCtx *pCtx = reinterpret_cast<DRMPresentCtx *>( data );

// Make this const when we move into CDRMBackend.
GetBackend()->PresentationFeedback().m_uCompletedPresents = pCtx->ulPendingFlipCount;
GetBackend()->GetCurrentConnector()->PresentationFeedback().m_uCompletedPresents = pCtx->ulPendingFlipCount;

if ( !g_DRM.pCRTC )
return;
Expand Down Expand Up @@ -772,7 +795,7 @@ static bool refresh_state( drm_t *drm )
drm->connectors.emplace(
std::piecewise_construct,
std::forward_as_tuple( uConnectorId ),
std::forward_as_tuple( pConnector ) );
std::forward_as_tuple( reinterpret_cast<gamescope::CDRMBackend *>( GetBackend() ), pConnector ) );
}
}

Expand Down Expand Up @@ -1945,8 +1968,9 @@ namespace gamescope
/////////////////////////
// CDRMConnector
/////////////////////////
CDRMConnector::CDRMConnector( drmModeConnector *pConnector )
CDRMConnector::CDRMConnector( CDRMBackend *pBackend, drmModeConnector *pConnector )
: CDRMAtomicTypedObject<DRM_MODE_OBJECT_CONNECTOR>( pConnector->connector_id )
, m_pBackend{ pBackend }
, m_pConnector{ pConnector, []( drmModeConnector *pConnector ){ drmModeFreeConnector( pConnector ); } }
{
RefreshState();
Expand Down Expand Up @@ -2025,6 +2049,11 @@ namespace gamescope
ParseEDID();
}

int CDRMConnector::Present( const FrameInfo_t *pFrameInfo, bool bAsync )
{
return HackyDRMPresent( pFrameInfo, bAsync );
}

void CDRMConnector::UpdateEffectiveOrientation( const drmModeModeInfo *pMode )
{
if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO )
Expand Down Expand Up @@ -2630,10 +2659,11 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI
{
drm_update_color_mgmt(drm);

const bool bVRRCapable = drm->pConnector && drm->pConnector->GetProperties().vrr_capable &&
drm->pCRTC && drm->pCRTC->GetProperties().VRR_ENABLED;
const bool bVRREnabled = bVRRCapable && frameInfo->allowVRR;
if ( bVRRCapable )
const bool bIsVRRCapable = drm->pConnector && drm->pConnector->GetProperties().vrr_capable && drm->pConnector->GetProperties().vrr_capable->GetCurrentValue();
const bool bHasVRREnable = drm->pCRTC && drm->pCRTC->GetProperties().VRR_ENABLED;

const bool bVRREnabled = bIsVRRCapable && bHasVRREnable && frameInfo->allowVRR;
if ( bIsVRRCapable )
{
if ( bVRREnabled != !!drm->pCRTC->GetProperties().VRR_ENABLED->GetCurrentValue() )
drm->needs_modeset = true;
Expand Down Expand Up @@ -3199,8 +3229,13 @@ namespace gamescope
return true;
}

virtual int Present( const FrameInfo_t *pFrameInfo, bool bAsync ) override
virtual int Present( const FrameInfo_t *pFrameInfo, bool bAsync )
{
static uint64_t s_ulLastTime = get_time_in_nanos();
uint64_t ulNow = get_time_in_nanos();
drm_log.debugf( "CDRMBackend::Present Begin: %lu -> delta: %lu", ulNow, ulNow - s_ulLastTime );
s_ulLastTime = ulNow;

bool bWantsPartialComposite = pFrameInfo->layerCount >= 3 && !kDisablePartialComposition;

static bool s_bWasFirstFrame = true;
Expand Down Expand Up @@ -3553,14 +3588,6 @@ namespace gamescope
return nullptr;
}

virtual bool IsVRRActive() const override
{
if ( !g_DRM.pCRTC || !g_DRM.pCRTC->GetProperties().VRR_ENABLED )
return false;

return !!g_DRM.pCRTC->GetProperties().VRR_ENABLED->GetCurrentValue();
}

virtual bool SupportsPlaneHardwareCursor() const override
{
return true;
Expand Down Expand Up @@ -3671,14 +3698,14 @@ namespace gamescope
drm->m_QueuedFbIds.swap( drm->m_FbIdsInRequest );
}

m_PresentFeedback.m_uQueuedPresents++;
GetCurrentConnector()->PresentationFeedback().m_uQueuedPresents++;

uint32_t uCurrentPresentCtx = m_uNextPresentCtx;
m_uNextPresentCtx = ( m_uNextPresentCtx + 1 ) % 3;
m_PresentCtxs[uCurrentPresentCtx].ulPendingFlipCount = m_PresentFeedback.m_uQueuedPresents;
m_PresentCtxs[uCurrentPresentCtx].ulPendingFlipCount = GetCurrentConnector()->PresentationFeedback().m_uQueuedPresents;

drm_log.debugf("flip commit %" PRIu64, (uint64_t)m_PresentFeedback.m_uQueuedPresents);
gpuvis_trace_printf( "flip commit %" PRIu64, (uint64_t)m_PresentFeedback.m_uQueuedPresents );
drm_log.debugf("flip commit %" PRIu64, (uint64_t)GetCurrentConnector()->PresentationFeedback().m_uQueuedPresents);
gpuvis_trace_printf( "flip commit %" PRIu64, (uint64_t)GetCurrentConnector()->PresentationFeedback().m_uQueuedPresents );

ret = drmModeAtomicCommit(drm->fd, drm->req, drm->flags, &m_PresentCtxs[uCurrentPresentCtx] );
if ( ret != 0 )
Expand All @@ -3704,7 +3731,7 @@ namespace gamescope
// Clear our refs.
drm->m_FbIdsInRequest.clear();

m_PresentFeedback.m_uQueuedPresents--;
GetCurrentConnector()->PresentationFeedback().m_uQueuedPresents--;

if ( isPageFlip )
drm->uPendingFlipCount--;
Expand Down Expand Up @@ -3775,3 +3802,9 @@ namespace gamescope
return Set( new CDRMBackend{} );
}
}

int HackyDRMPresent( const FrameInfo_t *pFrameInfo, bool bAsync )
{
return static_cast<gamescope::CDRMBackend *>( GetBackend() )->Present( pFrameInfo, bAsync );
}

Loading