From d960c797b62a2151c96f0ce13b4b077dfa2298c6 Mon Sep 17 00:00:00 2001 From: sharkautarch <128002472+sharkautarch@users.noreply.github.com> Date: Sat, 7 Dec 2024 16:40:12 -0500 Subject: [PATCH] steamcompmgr, DRM: fix two more coredump-at-exit bugs --- src/Backends/DRMBackend.cpp | 748 +++++++++++++++++++----------------- src/steamcompmgr.cpp | 19 +- 2 files changed, 409 insertions(+), 358 deletions(-) diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 0b121e8416..989e182629 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -418,75 +418,139 @@ namespace gamescope private: uint32_t m_uFbId = 0; }; -} + + struct DRMPresentCtx + { + uint64_t ulPendingFlipCount = 0; + }; + + struct drm_t { + bool bUseLiftoff; -struct saved_mode { - int width; - int height; - int refresh; -}; + int fd = -1; -struct drm_t { - bool bUseLiftoff; + int preferred_width, preferred_height, preferred_refresh; - int fd = -1; + uint64_t cursor_width, cursor_height; + bool allow_modifiers; + struct wlr_drm_format_set formats; - int preferred_width, preferred_height, preferred_refresh; + std::vector< std::unique_ptr< gamescope::CDRMPlane > > planes; + std::vector< std::unique_ptr< gamescope::CDRMCRTC > > crtcs; + std::unordered_map< uint32_t, gamescope::CDRMConnector > connectors; - uint64_t cursor_width, cursor_height; - bool allow_modifiers; - struct wlr_drm_format_set formats; + gamescope::CDRMPlane *pPrimaryPlane; + gamescope::CDRMCRTC *pCRTC; + gamescope::CDRMConnector *pConnector; - std::vector< std::unique_ptr< gamescope::CDRMPlane > > planes; - std::vector< std::unique_ptr< gamescope::CDRMCRTC > > crtcs; - std::unordered_map< uint32_t, gamescope::CDRMConnector > connectors; + struct wlr_drm_format_set primary_formats; - gamescope::CDRMPlane *pPrimaryPlane; - gamescope::CDRMCRTC *pCRTC; - gamescope::CDRMConnector *pConnector; + drmModeAtomicReq *req; + uint32_t flags; - struct wlr_drm_format_set primary_formats; + struct liftoff_device *lo_device; + struct liftoff_output *lo_output; + struct liftoff_layer *lo_layers[ k_nMaxLayers ]; - drmModeAtomicReq *req; - uint32_t flags; + std::shared_ptr sdr_static_metadata; - struct liftoff_device *lo_device; - struct liftoff_output *lo_output; - struct liftoff_layer *lo_layers[ k_nMaxLayers ]; + struct drm_state_t { + std::shared_ptr mode_id; + uint32_t color_mgmt_serial; + std::shared_ptr lut3d_id[ EOTF_Count ]; + std::shared_ptr shaperlut_id[ EOTF_Count ]; + amdgpu_transfer_function output_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT; + } current, pending; - std::shared_ptr sdr_static_metadata; + // FBs in the atomic request, but not yet submitted to KMS + // Accessed only on req thread + std::vector> m_FbIdsInRequest; - struct drm_state_t { - std::shared_ptr mode_id; - uint32_t color_mgmt_serial; - std::shared_ptr lut3d_id[ EOTF_Count ]; - std::shared_ptr shaperlut_id[ EOTF_Count ]; - amdgpu_transfer_function output_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT; - } current, pending; + // 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> m_QueuedFbIds; + // FBs currently on screen. + // Accessed only on page flip handler thread. + std::mutex m_mutVisibleFbIds; + std::vector> m_VisibleFbIds; - // FBs in the atomic request, but not yet submitted to KMS - // Accessed only on req thread - std::vector> m_FbIdsInRequest; + std::atomic < uint32_t > uPendingFlipCount = { 0 }; - // 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> m_QueuedFbIds; - // FBs currently on screen. - // Accessed only on page flip handler thread. - std::mutex m_mutVisibleFbIds; - std::vector> m_VisibleFbIds; + std::atomic < bool > paused = { false }; + std::atomic < int > out_of_date = { false }; + std::atomic < bool > needs_modeset = { false }; - std::atomic < uint32_t > uPendingFlipCount = { 0 }; + std::unordered_map< std::string, int > connector_priorities; - std::atomic < bool > paused = { false }; - std::atomic < int > out_of_date = { false }; - std::atomic < bool > needs_modeset = { false }; + char *device_name = nullptr; + }; + + class CDRMBackend final : public CBaseBackend + { + public: + CDRMBackend(); + virtual ~CDRMBackend(); + virtual bool Init() override; + virtual bool PostInit() override; + virtual std::span GetInstanceExtensions() const override; + virtual std::span GetDeviceExtensions( VkPhysicalDevice pVkPhysicalDevice ) const override; + virtual VkImageLayout GetPresentLayout() const override; + virtual void GetPreferredOutputFormat( uint32_t *pPrimaryPlaneFormat, uint32_t *pOverlayPlaneFormat ) const override; + virtual bool ValidPhysicalDevice( VkPhysicalDevice pVkPhysicalDevice ) const override; + virtual int Present( const FrameInfo_t *pFrameInfo, bool bAsync ) override; + virtual void DirtyState( bool bForce, bool bForceModeset ) override; + virtual bool PollState() override; + virtual std::shared_ptr CreateBackendBlob( const std::type_info &type, std::span data ) override; + virtual OwningRc ImportDmabufToBackend( wlr_buffer *pBuffer, wlr_dmabuf_attributes *pDmaBuf ) override; + virtual bool UsesModifiers() const override; + virtual std::span GetSupportedModifiers( uint32_t uDrmFormat ) const override; + virtual IBackendConnector *GetCurrentConnector() override; + virtual IBackendConnector *GetConnector( GamescopeScreenType eScreenType ) override; + virtual bool IsVRRActive() const override; + virtual bool SupportsPlaneHardwareCursor() const override; + virtual bool SupportsTearing() const override; + virtual bool UsesVulkanSwapchain() const override; + virtual bool IsSessionBased() const override; + virtual bool SupportsExplicitSync() const override; + virtual bool IsVisible() const override; + virtual glm::uvec2 CursorSurfaceSize( glm::uvec2 uvecSize ) const override; + virtual bool HackTemporarySetDynamicRefresh( int nRefresh ) override; + virtual void HackUpdatePatchedEdid() override; + + drm_t* GetDrmData() { + return &m_drm; + } - std::unordered_map< std::string, int > connector_priorities; + protected: + drm_t m_drm; + virtual void OnBackendBlobDestroyed( BackendBlob *pBlob ) override; - char *device_name = nullptr; + private: + bool m_bWasCompositing = false; + bool m_bWasPartialCompsiting = false; + int m_nLastSingleOverlayZPos = 0; + + uint32_t m_uNextPresentCtx = 0; + DRMPresentCtx m_PresentCtxs[3]; + + bool SupportsColorManagement() const; + int Commit( const FrameInfo_t *pFrameInfo ); + }; + + static inline CDRMBackend* GetDrmBackend() { + return static_cast(GetBackend()); + } +} + +struct saved_mode { + int width; + int height; + int refresh; }; +using gamescope::drm_t; +using gamescope::DRMPresentCtx; +using gamescope::GetDrmBackend; void drm_drop_fbid( struct drm_t *drm, uint32_t fbid ); bool drm_set_mode( struct drm_t *drm, const drmModeModeInfo *mode ); @@ -494,18 +558,12 @@ 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; extern bool g_bDebugLayers; -struct DRMPresentCtx -{ - uint64_t ulPendingFlipCount = 0; -}; - extern gamescope::ConVar cv_composite_force; extern bool g_bColorSliderInUse; extern bool fadingOut; @@ -516,7 +574,7 @@ extern std::string g_reshade_effect; #endif bool drm_update_color_mgmt(struct drm_t *drm); -bool drm_supports_color_mgmt(struct drm_t *drm); +bool drm_supports_color_mgmt(const struct drm_t *drm); bool drm_set_connector( struct drm_t *drm, gamescope::CDRMConnector *conn ); struct drm_color_ctm2 { @@ -684,10 +742,10 @@ static void page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsi // Make this const when we move into CDRMBackend. GetBackend()->PresentationFeedback().m_uCompletedPresents = pCtx->ulPendingFlipCount; - if ( !g_DRM.pCRTC ) + if ( !GetDrmBackend()->GetDrmData()->pCRTC ) return; - if ( g_DRM.pCRTC->GetObjectId() != crtc_id ) + if ( GetDrmBackend()->GetDrmData()->pCRTC->GetObjectId() != crtc_id ) return; static uint64_t ulLastVBlankTime = 0; @@ -704,14 +762,14 @@ static void page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsi ulLastVBlankTime = vblanktime; { - std::scoped_lock lock{ g_DRM.m_QueuedFbIdsMutex, g_DRM.m_mutVisibleFbIds }; + std::scoped_lock lock{ GetDrmBackend()->GetDrmData()->m_QueuedFbIdsMutex, GetDrmBackend()->GetDrmData()->m_mutVisibleFbIds }; // Swap and clear from queue -> visible to avoid allocations. - g_DRM.m_VisibleFbIds.swap( g_DRM.m_QueuedFbIds ); - g_DRM.m_QueuedFbIds.clear(); + GetDrmBackend()->GetDrmData()->m_VisibleFbIds.swap( GetDrmBackend()->GetDrmData()->m_QueuedFbIds ); + GetDrmBackend()->GetDrmData()->m_QueuedFbIds.clear(); } - g_DRM.uPendingFlipCount--; - g_DRM.uPendingFlipCount.notify_all(); + GetDrmBackend()->GetDrmData()->uPendingFlipCount--; + GetDrmBackend()->GetDrmData()->uPendingFlipCount.notify_all(); mangoapp_output_update( vblanktime ); @@ -724,7 +782,7 @@ void flip_handler_thread_run(void) pthread_setname_np( pthread_self(), "gamescope-kms" ); struct pollfd pollfd = { - .fd = g_DRM.fd, + .fd = GetDrmBackend()->GetDrmData()->fd, .events = POLLIN, }; @@ -740,7 +798,7 @@ void flip_handler_thread_run(void) .version = 3, .page_flip_handler2 = page_flip_handler, }; - drmHandleEvent(g_DRM.fd, &evctx); + drmHandleEvent(GetDrmBackend()->GetDrmData()->fd, &evctx); } } @@ -1817,7 +1875,7 @@ namespace gamescope template < uint32_t DRMObjectType > std::optional CDRMAtomicTypedObject::GetRawProperties() { - drmModeObjectProperties *pProperties = drmModeObjectGetProperties( g_DRM.fd, m_ulObjectId, DRMObjectType ); + drmModeObjectProperties *pProperties = drmModeObjectGetProperties( GetDrmBackend()->GetDrmData()->fd, m_ulObjectId, DRMObjectType ); if ( !pProperties ) { drm_log.errorf_errno( "drmModeObjectGetProperties failed" ); @@ -1828,7 +1886,7 @@ namespace gamescope DRMObjectRawProperties rawProperties; for ( uint32_t i = 0; i < pProperties->count_props; i++ ) { - drmModePropertyRes *pProperty = drmModeGetProperty( g_DRM.fd, pProperties->props[ i ] ); + drmModePropertyRes *pProperty = drmModeGetProperty( GetDrmBackend()->GetDrmData()->fd, pProperties->props[ i ] ); if ( !pProperty ) continue; defer( drmModeFreeProperty( pProperty ) ); @@ -1979,7 +2037,7 @@ namespace gamescope // TODO: Clean this up. m_pConnector = CAutoDeletePtr< drmModeConnector > { - drmModeGetConnector( g_DRM.fd, m_pConnector->connector_id ), + drmModeGetConnector( GetDrmBackend()->GetDrmData()->fd, m_pConnector->connector_id ), []( drmModeConnector *pConnector ){ drmModeFreeConnector( pConnector ); } }; @@ -2007,7 +2065,7 @@ namespace gamescope // Clear this information out. m_Mutable = MutableConnectorState{}; - m_Mutable.uPossibleCRTCMask = drmModeConnectorGetPossibleCrtcs( g_DRM.fd, GetModeConnector() ); + m_Mutable.uPossibleCRTCMask = drmModeConnectorGetPossibleCrtcs( GetDrmBackend()->GetDrmData()->fd, GetModeConnector() ); // These are string constants from libdrm, no free. const char *pszTypeStr = drmModeGetConnectorTypeName( GetModeConnector()->connector_type ); @@ -2095,7 +2153,7 @@ namespace gamescope if ( !ulBlobId ) return; - drmModePropertyBlobRes *pBlob = drmModeGetPropertyBlob( g_DRM.fd, ulBlobId ); + drmModePropertyBlobRes *pBlob = drmModeGetPropertyBlob( GetDrmBackend()->GetDrmData()->fd, ulBlobId ); if ( !pBlob ) return; defer( drmModeFreePropertyBlob( pBlob ) ); @@ -2370,7 +2428,7 @@ namespace gamescope CDRMFb::~CDRMFb() { // I own the fbid. - if ( drmModeRmFB( g_DRM.fd, m_uFbId ) != 0 ) + if ( drmModeRmFB( GetDrmBackend()->GetDrmData()->fd, m_uFbId ) != 0 ) drm_log.errorf_errno( "drmModeRmFB failed" ); m_uFbId = 0; } @@ -2696,7 +2754,7 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI bool bSinglePlane = frameInfo->layerCount < 2 && cv_drm_single_plane_optimizations; - if ( drm_supports_color_mgmt( &g_DRM ) && frameInfo->applyOutputColorMgmt ) + if ( drm_supports_color_mgmt( GetDrmBackend()->GetDrmData() ) && frameInfo->applyOutputColorMgmt ) { if ( !cv_drm_debug_disable_output_tf && !bSinglePlane ) { @@ -3053,10 +3111,10 @@ bool drm_set_refresh( struct drm_t *drm, int refresh ) } else { - if ( g_DRM.pConnector && g_DRM.pConnector->GetModeGenerator() ) + if ( drm->pConnector && drm->pConnector->GetModeGenerator() ) { const drmModeModeInfo *preferred_mode = find_mode(connector, 0, 0, 0); - mode = g_DRM.pConnector->GetModeGenerator()( preferred_mode, refresh ); + mode = drm->pConnector->GetModeGenerator()( preferred_mode, refresh ); } else { @@ -3145,7 +3203,7 @@ std::pair drm_get_connector_identifier(struct drm_t *drm) return std::make_pair(drm->pConnector->GetModeConnector()->connector_type, drm->pConnector->GetModeConnector()->connector_type_id); } -bool drm_supports_color_mgmt(struct drm_t *drm) +bool drm_supports_color_mgmt(const struct drm_t *drm) { if ( g_bForceDisableColorMgmt ) return false; @@ -3163,27 +3221,17 @@ std::span drm_get_valid_refresh_rates( struct drm_t *drm ) return std::span{}; } - namespace gamescope { - class CDRMBackend; - - class CDRMBackend final : public CBaseBackend + CDRMBackend::CDRMBackend() {} + CDRMBackend::~CDRMBackend() { - public: - CDRMBackend() - { - } - - virtual ~CDRMBackend() - { - if ( g_DRM.fd != -1 ) - finish_drm( &g_DRM ); - } - - virtual bool Init() override - { - if ( !vulkan_init( vulkan_get_instance(), VK_NULL_HANDLE ) ) + if ( m_drm.fd != -1 ) + finish_drm( &m_drm ); + } + bool CDRMBackend::Init() + { + if ( !vulkan_init( vulkan_get_instance(), VK_NULL_HANDLE ) ) { fprintf( stderr, "Failed to initialize Vulkan\n" ); return false; @@ -3195,43 +3243,42 @@ namespace gamescope return false; } - return init_drm( &g_DRM, g_nPreferredOutputWidth, g_nPreferredOutputHeight, g_nNestedRefresh ); - } - - virtual bool PostInit() override - { - if ( g_DRM.pConnector ) - WritePatchedEdid( g_DRM.pConnector->GetRawEDID(), g_DRM.pConnector->GetHDRInfo(), g_bRotated ); - return true; - } - - virtual std::span GetInstanceExtensions() const override - { - return std::span{}; - } - virtual std::span GetDeviceExtensions( VkPhysicalDevice pVkPhysicalDevice ) const override - { - return std::span{}; - } - virtual VkImageLayout GetPresentLayout() const override - { - // Does not matter, as this has a queue family transition - // to VK_QUEUE_FAMILY_FOREIGN_EXT queue, - // thus: newLayout is ignored. - return VK_IMAGE_LAYOUT_GENERAL; - } - virtual void GetPreferredOutputFormat( uint32_t *pPrimaryPlaneFormat, uint32_t *pOverlayPlaneFormat ) const override - { - *pPrimaryPlaneFormat = g_nDRMFormat; - *pOverlayPlaneFormat = g_nDRMFormatOverlay; - } - virtual bool ValidPhysicalDevice( VkPhysicalDevice pVkPhysicalDevice ) const override - { - return true; - } - - virtual int Present( const FrameInfo_t *pFrameInfo, bool bAsync ) override - { + return init_drm( &m_drm, g_nPreferredOutputWidth, g_nPreferredOutputHeight, g_nNestedRefresh ); + } + + bool CDRMBackend::PostInit() + { + if ( m_drm.pConnector ) + WritePatchedEdid( m_drm.pConnector->GetRawEDID(), m_drm.pConnector->GetHDRInfo(), g_bRotated ); + return true; + } + + std::span CDRMBackend::GetInstanceExtensions() const + { + return std::span{}; + } + std::span CDRMBackend::GetDeviceExtensions( VkPhysicalDevice pVkPhysicalDevice ) const + { + return std::span{}; + } + VkImageLayout CDRMBackend::GetPresentLayout() const + { + // Does not matter, as this has a queue family transition + // to VK_QUEUE_FAMILY_FOREIGN_EXT queue, + // thus: newLayout is ignored. + return VK_IMAGE_LAYOUT_GENERAL; + } + void CDRMBackend::GetPreferredOutputFormat( uint32_t *pPrimaryPlaneFormat, uint32_t *pOverlayPlaneFormat ) const + { + *pPrimaryPlaneFormat = g_nDRMFormat; + *pOverlayPlaneFormat = g_nDRMFormatOverlay; + } + bool CDRMBackend::ValidPhysicalDevice( VkPhysicalDevice pVkPhysicalDevice ) const + { + return true; + } + int CDRMBackend::Present( const FrameInfo_t *pFrameInfo, bool bAsync ) + { bool bWantsPartialComposite = pFrameInfo->layerCount >= 3 && !kDisablePartialComposition; static bool s_bWasFirstFrame = true; @@ -3281,7 +3328,7 @@ namespace gamescope bool bDoComposite = true; if ( !bNeedsFullComposite && !bWantsPartialComposite ) { - int ret = drm_prepare( &g_DRM, bAsync, pFrameInfo ); + int ret = drm_prepare( &m_drm, bAsync, pFrameInfo ); if ( ret == 0 ) bDoComposite = false; else if ( ret == -EACCES ) @@ -3464,7 +3511,7 @@ namespace gamescope m_bWasPartialCompsiting = true; } - int ret = drm_prepare( &g_DRM, bAsync, &presentCompFrameInfo ); + int ret = drm_prepare( &m_drm, bAsync, &presentCompFrameInfo ); // Happens when we're VT-switched away if ( ret == -EACCES ) @@ -3472,7 +3519,7 @@ namespace gamescope if ( ret != 0 ) { - if ( g_DRM.current.mode_id == 0 ) + if ( m_drm.current.mode_id == 0 ) { xwm_log.errorf("We failed our modeset and have no mode to fall back to! (Initial modeset failed?): %s", strerror(-ret)); abort(); @@ -3481,7 +3528,7 @@ namespace gamescope xwm_log.errorf("Failed to prepare 1-layer flip (%s), trying again with previous mode if modeset needed", strerror( -ret )); // Try once again to in case we need to fall back to another mode. - ret = drm_prepare( &g_DRM, bAsync, &compositeFrameInfo ); + ret = drm_prepare( &m_drm, bAsync, &compositeFrameInfo ); // Happens when we're VT-switched away if ( ret == -EACCES ) @@ -3504,297 +3551,284 @@ namespace gamescope } return Commit( &compositeFrameInfo ); - } - - virtual void DirtyState( bool bForce, bool bForceModeset ) override - { - if ( bForceModeset ) - g_DRM.needs_modeset = true; - g_DRM.out_of_date = std::max( g_DRM.out_of_date, bForce ? 2 : 1 ); - g_DRM.paused = !wlsession_active(); - } + } + void CDRMBackend::DirtyState( bool bForce, bool bForceModeset ) + { + if ( bForceModeset ) + m_drm.needs_modeset = true; + m_drm.out_of_date = std::max( m_drm.out_of_date, bForce ? 2 : 1 ); + m_drm.paused = !wlsession_active(); + } - virtual bool PollState() override - { - return drm_poll_state( &g_DRM ); - } + bool CDRMBackend::PollState() + { + return drm_poll_state( &m_drm ); + } - virtual std::shared_ptr CreateBackendBlob( const std::type_info &type, std::span data ) override + std::shared_ptr CDRMBackend::CreateBackendBlob( const std::type_info &type, std::span data ) + { + uint32_t uBlob = 0; + if ( type == typeid( glm::mat3x4 ) ) { - uint32_t uBlob = 0; - if ( type == typeid( glm::mat3x4 ) ) - { - assert( data.size() == sizeof( glm::mat3x4 ) ); - - drm_color_ctm2 ctm2; - const float *pData = reinterpret_cast( data.data() ); - for ( uint32_t i = 0; i < 12; i++ ) - ctm2.matrix[i] = drm_calc_s31_32( pData[i] ); + assert( data.size() == sizeof( glm::mat3x4 ) ); - if ( drmModeCreatePropertyBlob( g_DRM.fd, reinterpret_cast( &ctm2 ), sizeof( ctm2 ), &uBlob ) != 0 ) - return nullptr; - } - else - { - if ( drmModeCreatePropertyBlob( g_DRM.fd, data.data(), data.size(), &uBlob ) != 0 ) - return nullptr; - } + drm_color_ctm2 ctm2; + const float *pData = reinterpret_cast( data.data() ); + for ( uint32_t i = 0; i < 12; i++ ) + ctm2.matrix[i] = drm_calc_s31_32( pData[i] ); - return std::make_shared( data, uBlob, true ); + if ( drmModeCreatePropertyBlob( m_drm.fd, reinterpret_cast( &ctm2 ), sizeof( ctm2 ), &uBlob ) != 0 ) + return nullptr; } - - virtual OwningRc ImportDmabufToBackend( wlr_buffer *pBuffer, wlr_dmabuf_attributes *pDmaBuf ) override + else { - return drm_fbid_from_dmabuf( &g_DRM, pBuffer, pDmaBuf ); + if ( drmModeCreatePropertyBlob( m_drm.fd, data.data(), data.size(), &uBlob ) != 0 ) + return nullptr; } - virtual bool UsesModifiers() const override - { - return g_DRM.allow_modifiers; - } - virtual std::span GetSupportedModifiers( uint32_t uDrmFormat ) const override - { - const wlr_drm_format *pFormat = wlr_drm_format_set_get( &g_DRM.formats, uDrmFormat ); - if ( !pFormat ) - return std::span{}; + return std::make_shared( data, uBlob, true ); + } - return std::span{ pFormat->modifiers, pFormat->modifiers + pFormat->len }; - } + OwningRc CDRMBackend::ImportDmabufToBackend( wlr_buffer *pBuffer, wlr_dmabuf_attributes *pDmaBuf ) + { + return drm_fbid_from_dmabuf( &m_drm, pBuffer, pDmaBuf ); + } - virtual IBackendConnector *GetCurrentConnector() override - { - return g_DRM.pConnector; - } + bool CDRMBackend::UsesModifiers() const + { + return m_drm.allow_modifiers; + } + std::span CDRMBackend::GetSupportedModifiers( uint32_t uDrmFormat ) const + { + const wlr_drm_format *pFormat = wlr_drm_format_set_get( &m_drm.formats, uDrmFormat ); + if ( !pFormat ) + return std::span{}; - virtual IBackendConnector *GetConnector( GamescopeScreenType eScreenType ) override - { - if ( GetCurrentConnector() && GetCurrentConnector()->GetScreenType() == eScreenType ) - return GetCurrentConnector(); + return std::span{ pFormat->modifiers, pFormat->modifiers + pFormat->len }; + } + + IBackendConnector *CDRMBackend::GetCurrentConnector() + { + return m_drm.pConnector; + } - if ( eScreenType == GAMESCOPE_SCREEN_TYPE_INTERNAL ) + IBackendConnector *CDRMBackend::GetConnector( GamescopeScreenType eScreenType ) + { + if ( GetCurrentConnector() && GetCurrentConnector()->GetScreenType() == eScreenType ) + return GetCurrentConnector(); + + if ( eScreenType == GAMESCOPE_SCREEN_TYPE_INTERNAL ) + { + for ( auto &iter : m_drm.connectors ) { - for ( auto &iter : g_DRM.connectors ) - { - gamescope::CDRMConnector *pConnector = &iter.second; - if ( pConnector->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL ) - return pConnector; - } + gamescope::CDRMConnector *pConnector = &iter.second; + if ( pConnector->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL ) + return pConnector; } - - return nullptr; } - virtual bool IsVRRActive() const override - { - if ( !g_DRM.pCRTC || !g_DRM.pCRTC->GetProperties().VRR_ENABLED ) - return false; + return nullptr; + } - return !!g_DRM.pCRTC->GetProperties().VRR_ENABLED->GetCurrentValue(); - } + bool CDRMBackend::IsVRRActive() const + { + if ( !m_drm.pCRTC || !m_drm.pCRTC->GetProperties().VRR_ENABLED ) + return false; - virtual bool SupportsPlaneHardwareCursor() const override - { - return true; - } + return !!m_drm.pCRTC->GetProperties().VRR_ENABLED->GetCurrentValue(); + } - virtual bool SupportsTearing() const override - { - return g_bSupportsAsyncFlips; - } + bool CDRMBackend::SupportsPlaneHardwareCursor() const + { + return true; + } - virtual bool UsesVulkanSwapchain() const override - { - return false; - } + bool CDRMBackend::SupportsTearing() const + { + return g_bSupportsAsyncFlips; + } - virtual bool IsSessionBased() const override - { - return true; - } + bool CDRMBackend::UsesVulkanSwapchain() const + { + return false; + } - virtual bool SupportsExplicitSync() const override - { + bool CDRMBackend::IsSessionBased() const + { + return true; + } + + bool CDRMBackend::SupportsExplicitSync() const + { #if __linux__ - auto [nMajor, nMinor, nPatch] = GetKernelVersion(); - - // Only expose support on 6.8+ for eventfd fixes. - if ( nMajor < 6 ) - return false; + auto [nMajor, nMinor, nPatch] = GetKernelVersion(); + + // Only expose support on 6.8+ for eventfd fixes. + if ( nMajor < 6 ) + return false; - if ( nMajor == 6 && nMinor < 8 ) - return false; -#else - // I don't know about this for FreeBSD, etc. + if ( nMajor == 6 && nMinor < 8 ) return false; +#else + // I don't know about this for FreeBSD, etc. + return false; #endif - return g_bSupportsSyncObjs && !cv_drm_debug_disable_explicit_sync; - } + return g_bSupportsSyncObjs && !cv_drm_debug_disable_explicit_sync; + } - virtual bool IsVisible() const override - { - return !g_DRM.paused; - } + bool CDRMBackend::IsVisible() const + { + return !m_drm.paused; + } - virtual glm::uvec2 CursorSurfaceSize( glm::uvec2 uvecSize ) const override - { - if ( !k_bUseCursorPlane ) - return uvecSize; + glm::uvec2 CDRMBackend::CursorSurfaceSize( glm::uvec2 uvecSize ) const + { + if ( !k_bUseCursorPlane ) + return uvecSize; - return glm::uvec2{ g_DRM.cursor_width, g_DRM.cursor_height }; - } + return glm::uvec2{ m_drm.cursor_width, m_drm.cursor_height }; + } - virtual bool HackTemporarySetDynamicRefresh( int nRefresh ) override - { - return drm_set_refresh( &g_DRM, nRefresh ); - } + bool CDRMBackend::HackTemporarySetDynamicRefresh( int nRefresh ) + { + return drm_set_refresh( &m_drm, nRefresh ); + } - virtual void HackUpdatePatchedEdid() override - { - if ( !GetCurrentConnector() ) - return; + void CDRMBackend::HackUpdatePatchedEdid() + { + if ( !GetCurrentConnector() ) + return; - WritePatchedEdid( GetCurrentConnector()->GetRawEDID(), GetCurrentConnector()->GetHDRInfo(), g_bRotated ); - } + WritePatchedEdid( GetCurrentConnector()->GetRawEDID(), GetCurrentConnector()->GetHDRInfo(), g_bRotated ); + } + + void CDRMBackend::OnBackendBlobDestroyed( BackendBlob *pBlob ) + { + if ( pBlob->GetBlobValue() ) + drmModeDestroyPropertyBlob( m_drm.fd, pBlob->GetBlobValue() ); + } + + bool CDRMBackend::SupportsColorManagement() const + { + return drm_supports_color_mgmt( &m_drm ); + } - protected: + int CDRMBackend::Commit( const FrameInfo_t *pFrameInfo ) + { + int ret = 0; - virtual void OnBackendBlobDestroyed( BackendBlob *pBlob ) override - { - if ( pBlob->GetBlobValue() ) - drmModeDestroyPropertyBlob( g_DRM.fd, pBlob->GetBlobValue() ); - } + assert( m_drm.req != nullptr ); - private: - bool m_bWasCompositing = false; - bool m_bWasPartialCompsiting = false; - int m_nLastSingleOverlayZPos = 0; + defer( if ( m_drm.req != nullptr ) { drmModeAtomicFree( m_drm.req ); m_drm.req = nullptr; } ); - uint32_t m_uNextPresentCtx = 0; - DRMPresentCtx m_PresentCtxs[3]; + bool isPageFlip = m_drm.flags & DRM_MODE_PAGE_FLIP_EVENT; + uint32_t uNewPendingFlipCount = 0; - bool SupportsColorManagement() const + if ( isPageFlip ) { - return drm_supports_color_mgmt( &g_DRM ); - } + uNewPendingFlipCount = ++m_drm.uPendingFlipCount; - int Commit( const FrameInfo_t *pFrameInfo ) - { - drm_t *drm = &g_DRM; - int ret = 0; + // Do it before the commit, as otherwise the pageflip handler could + // potentially beat us to the refcount checks. - assert( drm->req != nullptr ); + // Swap over request FDs -> Queue + std::unique_lock lock( m_drm.m_QueuedFbIdsMutex ); + m_drm.m_QueuedFbIds.swap( m_drm.m_FbIdsInRequest ); + } - defer( if ( drm->req != nullptr ) { drmModeAtomicFree( drm->req ); drm->req = nullptr; } ); + m_PresentFeedback.m_uQueuedPresents++; - bool isPageFlip = drm->flags & DRM_MODE_PAGE_FLIP_EVENT; - uint32_t uNewPendingFlipCount = 0; + uint32_t uCurrentPresentCtx = m_uNextPresentCtx; + m_uNextPresentCtx = ( m_uNextPresentCtx + 1 ) % 3; + m_PresentCtxs[uCurrentPresentCtx].ulPendingFlipCount = m_PresentFeedback.m_uQueuedPresents; - if ( isPageFlip ) - { - uNewPendingFlipCount = ++drm->uPendingFlipCount; + drm_log.debugf("flip commit %" PRIu64, (uint64_t)m_PresentFeedback.m_uQueuedPresents); + gpuvis_trace_printf( "flip commit %" PRIu64, (uint64_t)m_PresentFeedback.m_uQueuedPresents ); - // Do it before the commit, as otherwise the pageflip handler could - // potentially beat us to the refcount checks. + ret = drmModeAtomicCommit(m_drm.fd, m_drm.req, m_drm.flags, &m_PresentCtxs[uCurrentPresentCtx] ); + if ( ret != 0 ) + { + drm_log.errorf_errno( "flip error" ); - // Swap over request FDs -> Queue - std::unique_lock lock( drm->m_QueuedFbIdsMutex ); - drm->m_QueuedFbIds.swap( drm->m_FbIdsInRequest ); + if ( ret != -EBUSY && ret != -EACCES ) + { + drm_log.errorf( "fatal flip error, aborting" ); + if ( isPageFlip ) + m_drm.uPendingFlipCount--; + abort(); } - m_PresentFeedback.m_uQueuedPresents++; + drm_rollback( &m_drm ); - uint32_t uCurrentPresentCtx = m_uNextPresentCtx; - m_uNextPresentCtx = ( m_uNextPresentCtx + 1 ) % 3; - m_PresentCtxs[uCurrentPresentCtx].ulPendingFlipCount = m_PresentFeedback.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 ); - - ret = drmModeAtomicCommit(drm->fd, drm->req, drm->flags, &m_PresentCtxs[uCurrentPresentCtx] ); - if ( ret != 0 ) + // Swap back over to what was previously queued (probably nothing) + // if this commit failed. { - drm_log.errorf_errno( "flip error" ); - - if ( ret != -EBUSY && ret != -EACCES ) - { - drm_log.errorf( "fatal flip error, aborting" ); - if ( isPageFlip ) - drm->uPendingFlipCount--; - abort(); - } - - drm_rollback( drm ); - - // Swap back over to what was previously queued (probably nothing) - // if this commit failed. - { - std::unique_lock lock( drm->m_QueuedFbIdsMutex ); - drm->m_QueuedFbIds.swap( drm->m_FbIdsInRequest ); - } - // Clear our refs. - drm->m_FbIdsInRequest.clear(); + std::unique_lock lock( m_drm.m_QueuedFbIdsMutex ); + m_drm.m_QueuedFbIds.swap( m_drm.m_FbIdsInRequest ); + } + // Clear our refs. + m_drm.m_FbIdsInRequest.clear(); - m_PresentFeedback.m_uQueuedPresents--; + m_PresentFeedback.m_uQueuedPresents--; - if ( isPageFlip ) - drm->uPendingFlipCount--; + if ( isPageFlip ) + m_drm.uPendingFlipCount--; - return ret; - } else { - // Our request went through! - // Clear what we swapped with (what was previously queued) - drm->m_FbIdsInRequest.clear(); + return ret; + } else { + // Our request went through! + // Clear what we swapped with (what was previously queued) + m_drm.m_FbIdsInRequest.clear(); - drm->current = drm->pending; + m_drm.current = m_drm.pending; - for ( std::unique_ptr< gamescope::CDRMCRTC > &pCRTC : drm->crtcs ) + for ( std::unique_ptr< gamescope::CDRMCRTC > &pCRTC : m_drm.crtcs ) + { + for ( std::optional &oProperty : pCRTC->GetProperties() ) { - for ( std::optional &oProperty : pCRTC->GetProperties() ) - { - if ( oProperty ) - oProperty->OnCommit(); - } + if ( oProperty ) + oProperty->OnCommit(); } + } - for ( std::unique_ptr< gamescope::CDRMPlane > &pPlane : drm->planes ) + for ( std::unique_ptr< gamescope::CDRMPlane > &pPlane : m_drm.planes ) + { + for ( std::optional &oProperty : pPlane->GetProperties() ) { - for ( std::optional &oProperty : pPlane->GetProperties() ) - { - if ( oProperty ) - oProperty->OnCommit(); - } + if ( oProperty ) + oProperty->OnCommit(); } + } - for ( auto &iter : drm->connectors ) + for ( auto &iter : m_drm.connectors ) + { + gamescope::CDRMConnector *pConnector = &iter.second; + for ( std::optional &oProperty : pConnector->GetProperties() ) { - gamescope::CDRMConnector *pConnector = &iter.second; - for ( std::optional &oProperty : pConnector->GetProperties() ) - { - if ( oProperty ) - oProperty->OnCommit(); - } + if ( oProperty ) + oProperty->OnCommit(); } } + } - // Update the draw time - // Ideally this would be updated by something right before the page flip - // is queued and would end up being the new page flip, rather than here. - // However, the page flip handler is called when the page flip occurs, - // not when it is successfully queued. - GetVBlankTimer().UpdateLastDrawTime( get_time_in_nanos() - g_SteamCompMgrVBlankTime.ulWakeupTime ); + // Update the draw time + // Ideally this would be updated by something right before the page flip + // is queued and would end up being the new page flip, rather than here. + // However, the page flip handler is called when the page flip occurs, + // not when it is successfully queued. + GetVBlankTimer().UpdateLastDrawTime( get_time_in_nanos() - g_SteamCompMgrVBlankTime.ulWakeupTime ); - if ( isPageFlip ) - { - // Wait for bPendingFlip to change from true -> false. - drm->uPendingFlipCount.wait( uNewPendingFlipCount ); - assert( drm->uPendingFlipCount == 0 ); - } - - return ret; + if ( isPageFlip ) + { + // Wait for bPendingFlip to change from true -> false. + m_drm.uPendingFlipCount.wait( uNewPendingFlipCount ); + assert( m_drm.uPendingFlipCount == 0 ); } - }; + return ret; + } + ///////////////////////// // Backend Instantiator diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 8d15bda9d2..a2e3b26e08 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -5898,6 +5898,23 @@ error(Display *dpy, XErrorEvent *ev) [[noreturn]] static void steamcompmgr_exit(std::optional> lock = std::nullopt) { + + // Need to clear all the vk_lutxd references (which can be tied to backend-allocated memory) + // for the colormgmt globals/statics, to avoid coredump at exit from within the colormgmt exit-time destructors: + for (auto& colorMgmtArr : + { + std::ref(g_ColorMgmtLuts), + std::ref(g_ColorMgmtLutsOverride), + std::ref(g_ScreenshotColorMgmtLuts), + std::ref(g_ScreenshotColorMgmtLutsHDR) + }) + { + for (auto& colorMgmt : colorMgmtArr.get()) + { + colorMgmt.gamescope_color_mgmt_luts::~gamescope_color_mgmt_luts(); //dtor call also calls all the subobjects' dtors + } + } + g_ImageWaiter.Shutdown(); // Clean up any commits. @@ -5922,7 +5939,7 @@ steamcompmgr_exit(std::optional> lock = std::nullop { g_ColorMgmt.pending.appHDRMetadata = nullptr; g_ColorMgmt.current.appHDRMetadata = nullptr; - + g_ColorMgmt.gamescope_color_mgmt_tracker_t::~gamescope_color_mgmt_tracker_t(); s_scRGB709To2020Matrix = nullptr; for (int i = 0; i < gamescope::GAMESCOPE_SCREEN_TYPE_COUNT; i++) {