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

Merged
merged 28 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
70364c0
all: Support for multiple virtual connectors
misyltoad Aug 22, 2024
ae445f6
main: Add --virtual-connector-strategy
misyltoad Aug 22, 2024
75eb179
OpeNVRBackend: Add vr_nudge_to_visible_per_connector
misyltoad Aug 22, 2024
a2e49de
OpenVRBackend: Add logging for overlay visible count
misyltoad Aug 23, 2024
f787cc6
OpenVRBackend: Consider visible when an appid's scene app is visible
misyltoad Aug 23, 2024
0e2f5c7
OpenVRBackend: Add vr-app-overlay-key
misyltoad Aug 23, 2024
3967cac
steamcompmgr: Clean up g_VirtualConnectorFocuses before closing backe…
misyltoad Aug 23, 2024
fe44d88
OpenVRBackend: Add logging for creating a new dashboard overlay
misyltoad Aug 23, 2024
4b86596
OpenVRBackend: Fix crash with SteamVR input thread
misyltoad Aug 23, 2024
831705b
SDLBackend: Fix with virtual connector backend
misyltoad Aug 26, 2024
9886583
steamcompmgr: Fix Steam intergration with multiple virtual connectors
misyltoad Aug 27, 2024
cf43175
LibInputHandler: add, hook up to vr-session-manager
misyltoad Aug 28, 2024
42e1fbd
LibInputHandler: Add support for scroll wheel
misyltoad Aug 28, 2024
3508441
OpenVRBackend: Support for physical mouse input controlling cursor
misyltoad Aug 28, 2024
1f3f118
OpenVRBackend: Only override cursor if not in relative mode.
misyltoad Aug 28, 2024
fe83b24
OpenVRBackend: Fix OverlayClosed when not in steam mode
misyltoad Sep 11, 2024
d76a94e
messagey: Add GAMESCOPE_ZENITY_DISABLE
misyltoad Sep 11, 2024
7ac836a
OpenVRBackend: Fix for dual/double frame jitter
misyltoad Nov 21, 2024
58abfaf
OpenVRBackend: Fix FPS limit not updating
misyltoad Nov 21, 2024
5cf6b91
GAMESCOPE_MANGOAPP_SOCKET_DISABLE added
Sep 12, 2024
c7cf2fb
OpenVRBackend: Disable explicit sync for now
misyltoad Nov 21, 2024
20ccd6f
Revert "steamcompmgr: Fix crash when using magnifier and game recording"
misyltoad Nov 21, 2024
f6da6a3
backend: Hack
misyltoad Nov 21, 2024
214041a
OpenVRBackend: Add mutex around fb id tracking
misyltoad Nov 22, 2024
1c92741
mangoapp: plumb engineName
pac85 Dec 3, 2024
6727265
OpenVRBackend: Handle quit more robustly
misyltoad Dec 14, 2024
1e30e9d
Revert "OpenVRBackend: Disable explicit sync for now"
pac85 Jan 10, 2025
778d592
steamcompmgr: Fix warning
misyltoad Jan 15, 2025
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
9 changes: 8 additions & 1 deletion layer/VkLayer_FROG_gamescope_wsi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ namespace GamescopeWSILayer {
struct GamescopeInstanceData {
wl_display* display;
uint32_t appId = 0;
std::string engineName;
GamescopeLayerClient::Flags flags = 0;
};
VKROOTS_DEFINE_SYNCHRONIZED_MAP_TYPE(GamescopeInstance, VkInstance);
Expand Down Expand Up @@ -616,9 +617,14 @@ namespace GamescopeWSILayer {
{
uint32_t appId = clientAppId();

std::string engineName;
if (pCreateInfo->pApplicationInfo && pCreateInfo->pApplicationInfo->pEngineName)
engineName = pCreateInfo->pApplicationInfo->pEngineName;

auto state = GamescopeInstance::create(*pInstance, GamescopeInstanceData {
.display = display,
.appId = appId,
.engineName = engineName,
.flags = defaultLayerClientFlags(pCreateInfo->pApplicationInfo, appId),
});

Expand Down Expand Up @@ -1251,7 +1257,8 @@ namespace GamescopeWSILayer {
uint32_t(pCreateInfo->imageColorSpace),
uint32_t(pCreateInfo->compositeAlpha),
uint32_t(pCreateInfo->preTransform),
uint32_t(pCreateInfo->clipped));
uint32_t(pCreateInfo->clipped),
gamescopeInstance->engineName.c_str());

return VK_SUCCESS;
}
Expand Down
1 change: 1 addition & 0 deletions protocol/gamescope-swapchain.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
<arg name="vk_composite_alpha" type="uint" summary="VkCompositeAlphaFlagBitsKHR of swapchain"/>
<arg name="vk_pre_transform" type="uint" summary="VkSurfaceTransformFlagBitsKHR of swapchain"/>
<arg name="vk_clipped" type="uint" summary="clipped (VkBool32) of swapchain"/>
<arg name="vk_engine_name" type="string" summary="Engine name"/>
</request>

<request name="set_present_mode">
Expand Down
228 changes: 130 additions & 98 deletions src/Backends/DRMBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,96 @@ gamescope::ConVar<bool> cv_drm_debug_disable_color_range( "drm_debug_disable_col
gamescope::ConVar<bool> cv_drm_debug_disable_explicit_sync( "drm_debug_disable_explicit_sync", false, "Force disable explicit sync on the DRM backend." );
gamescope::ConVar<bool> cv_drm_debug_disable_in_fence_fd( "drm_debug_disable_in_fence_fd", false, "Force disable IN_FENCE_FD being set to avoid over-synchronization on the DRM backend." );


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 @@ -259,10 +347,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 @@ -343,6 +431,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 @@ -371,17 +467,20 @@ namespace gamescope
}
}

void UpdateEffectiveOrientation( const drmModeModeInfo *pMode );
virtual int Present( const FrameInfo_t *pFrameInfo, bool bAsync ) override;

using DRMModeGenerator = std::function<drmModeModeInfo(const drmModeModeInfo *, int)>;
const DRMModeGenerator &GetModeGenerator() const
{
return m_Mutable.fnDynamicModeGenerator;
}
void UpdateEffectiveOrientation( const drmModeModeInfo *pMode );

private:
void ParseEDID();


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

struct MutableConnectorState
Expand All @@ -393,8 +492,8 @@ namespace gamescope
char szMakePNP[4]{};
char szModel[16]{};
const char *pszMake = ""; // Not owned, no free. This is a pointer to pnp db or szMakePNP.
std::vector<uint32_t> ValidDynamicRefreshRates{};
DRMModeGenerator fnDynamicModeGenerator;
std::vector<uint32_t> ValidDynamicRefreshRates{};
std::vector<uint8_t> EdidData; // Raw, unmodified.
std::vector<BackendMode> BackendModes;

Expand All @@ -420,82 +519,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 @@ -560,7 +583,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 @@ -682,7 +705,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 @@ -768,7 +791,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 @@ -1963,8 +1986,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 @@ -2043,6 +2067,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 @@ -3230,8 +3259,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 @@ -3584,14 +3618,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 @@ -3702,14 +3728,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 @@ -3735,7 +3761,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 @@ -3806,3 +3832,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
Loading