diff --git a/src/Backends/HeadlessBackend.cpp b/src/Backends/HeadlessBackend.cpp index 49987f69f..1bf4171a2 100644 --- a/src/Backends/HeadlessBackend.cpp +++ b/src/Backends/HeadlessBackend.cpp @@ -1,267 +1,11 @@ #include "backend.h" +#include "Backends/HeadlessBackend.hpp" #include "rendervulkan.hpp" #include "wlserver.hpp" #include "refresh_rate.h" -extern int g_nPreferredOutputWidth; -extern int g_nPreferredOutputHeight; - -namespace gamescope +namespace gamescope { - class CHeadlessConnector final : public IBackendConnector - { - public: - CHeadlessConnector() - { - } - virtual ~CHeadlessConnector() - { - } - - virtual gamescope::GamescopeScreenType GetScreenType() const override - { - return GAMESCOPE_SCREEN_TYPE_INTERNAL; - } - virtual GamescopePanelOrientation GetCurrentOrientation() const override - { - return GAMESCOPE_PANEL_ORIENTATION_0; - } - virtual bool SupportsHDR() const override - { - return false; - } - virtual bool IsHDRActive() const override - { - return false; - } - virtual const BackendConnectorHDRInfo &GetHDRInfo() const override - { - return m_HDRInfo; - } - virtual std::span GetModes() const override - { - return std::span{}; - } - - virtual bool SupportsVRR() const override - { - return false; - } - - virtual std::span GetRawEDID() const override - { - return std::span{}; - } - virtual std::span GetValidDynamicRefreshRates() const override - { - return std::span{}; - } - - virtual void GetNativeColorimetry( - bool bHDR10, - displaycolorimetry_t *displayColorimetry, EOTF *displayEOTF, - displaycolorimetry_t *outputEncodingColorimetry, EOTF *outputEncodingEOTF ) const override - { - *displayColorimetry = displaycolorimetry_709; - *displayEOTF = EOTF_Gamma22; - *outputEncodingColorimetry = displaycolorimetry_709; - *outputEncodingEOTF = EOTF_Gamma22; - } - - virtual const char *GetName() const override - { - return "Headless"; - } - virtual const char *GetMake() const override - { - return "Gamescope"; - } - virtual const char *GetModel() const override - { - return "Virtual Display"; - } - - private: - BackendConnectorHDRInfo m_HDRInfo{}; - }; - - class CHeadlessBackend final : public CBaseBackend - { - public: - CHeadlessBackend() - { - } - - virtual ~CHeadlessBackend() - { - } - - virtual bool Init() override - { - g_nOutputWidth = g_nPreferredOutputWidth; - g_nOutputHeight = g_nPreferredOutputHeight; - g_nOutputRefresh = g_nNestedRefresh; - - if ( g_nOutputHeight == 0 ) - { - if ( g_nOutputWidth != 0 ) - { - fprintf( stderr, "Cannot specify -W without -H\n" ); - return false; - } - g_nOutputHeight = 720; - } - if ( g_nOutputWidth == 0 ) - g_nOutputWidth = g_nOutputHeight * 16 / 9; - if ( g_nOutputRefresh == 0 ) - g_nOutputRefresh = ConvertHztomHz( 60 ); - - if ( !vulkan_init( vulkan_get_instance(), VK_NULL_HANDLE ) ) - { - return false; - } - - if ( !wlsession_init() ) - { - fprintf( stderr, "Failed to initialize Wayland session\n" ); - return false; - } - - return true; - } - - virtual bool PostInit() override - { - 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 - { - return VK_IMAGE_LAYOUT_GENERAL; - } - virtual void GetPreferredOutputFormat( uint32_t *pPrimaryPlaneFormat, uint32_t *pOverlayPlaneFormat ) const override - { - *pPrimaryPlaneFormat = VulkanFormatToDRM( VK_FORMAT_A2B10G10R10_UNORM_PACK32 ); - *pOverlayPlaneFormat = VulkanFormatToDRM( VK_FORMAT_B8G8R8A8_UNORM ); - } - virtual bool ValidPhysicalDevice( VkPhysicalDevice pVkPhysicalDevice ) const override - { - return true; - } - - virtual int Present( const FrameInfo_t *pFrameInfo, bool bAsync ) override - { - return 0; - } - - virtual void DirtyState( bool bForce, bool bForceModeset ) override - { - } - - virtual bool PollState() override - { - return false; - } - - virtual std::shared_ptr CreateBackendBlob( const std::type_info &type, std::span data ) override - { - return std::make_shared( data ); - } - - virtual OwningRc ImportDmabufToBackend( wlr_buffer *pBuffer, wlr_dmabuf_attributes *pDmaBuf ) override - { - return new CBaseBackendFb(); - } - - virtual bool UsesModifiers() const override - { - return false; - } - virtual std::span GetSupportedModifiers( uint32_t uDrmFormat ) const override - { - return std::span{}; - } - - virtual IBackendConnector *GetCurrentConnector() override - { - return &m_Connector; - } - virtual IBackendConnector *GetConnector( GamescopeScreenType eScreenType ) override - { - if ( eScreenType == GAMESCOPE_SCREEN_TYPE_INTERNAL ) - return &m_Connector; - - return nullptr; - } - - virtual bool IsVRRActive() const override - { - return false; - } - - virtual bool SupportsPlaneHardwareCursor() const override - { - return false; - } - - virtual bool SupportsTearing() const override - { - return false; - } - - virtual bool UsesVulkanSwapchain() const override - { - return false; - } - - virtual bool IsSessionBased() const override - { - return false; - } - - virtual bool SupportsExplicitSync() const override - { - return true; - } - - virtual bool IsVisible() const override - { - return true; - } - - virtual glm::uvec2 CursorSurfaceSize( glm::uvec2 uvecSize ) const override - { - return uvecSize; - } - - virtual bool HackTemporarySetDynamicRefresh( int nRefresh ) override - { - return false; - } - - virtual void HackUpdatePatchedEdid() override - { - } - - protected: - - virtual void OnBackendBlobDestroyed( BackendBlob *pBlob ) override - { - } - - private: - - CHeadlessConnector m_Connector; - }; - ///////////////////////// // Backend Instantiator ///////////////////////// diff --git a/src/Backends/HeadlessBackend.hpp b/src/Backends/HeadlessBackend.hpp new file mode 100644 index 000000000..4d1abb915 --- /dev/null +++ b/src/Backends/HeadlessBackend.hpp @@ -0,0 +1,269 @@ +VkInstance vulkan_get_instance( void ); +enum VkFormat; +uint32_t VulkanFormatToDRM( VkFormat vkFormat, std::optional obHasAlphaOverride ); +bool vulkan_init( VkInstance instance, VkSurfaceKHR surface ); +bool wlsession_init( void ); +extern int g_nPreferredOutputWidth; +extern int g_nPreferredOutputHeight; +extern uint32_t g_nOutputWidth; +extern uint32_t g_nOutputHeight; +extern int g_nOutputRefresh; +extern int g_nNestedRefresh; +#include "refresh_rate.h" + +namespace gamescope +{ + class CHeadlessConnector final : public IBackendConnector + { + public: + CHeadlessConnector() + { + } + virtual ~CHeadlessConnector() + { + } + + virtual gamescope::GamescopeScreenType GetScreenType() const override + { + return GAMESCOPE_SCREEN_TYPE_INTERNAL; + } + virtual GamescopePanelOrientation GetCurrentOrientation() const override + { + return GAMESCOPE_PANEL_ORIENTATION_0; + } + virtual bool SupportsHDR() const override + { + return false; + } + virtual bool IsHDRActive() const override + { + return false; + } + virtual const BackendConnectorHDRInfo &GetHDRInfo() const override + { + return m_HDRInfo; + } + virtual std::span GetModes() const override + { + return std::span{}; + } + + virtual bool SupportsVRR() const override + { + return false; + } + + virtual std::span GetRawEDID() const override + { + return std::span{}; + } + virtual std::span GetValidDynamicRefreshRates() const override + { + return std::span{}; + } + + virtual void GetNativeColorimetry( + bool bHDR10, + displaycolorimetry_t *displayColorimetry, EOTF *displayEOTF, + displaycolorimetry_t *outputEncodingColorimetry, EOTF *outputEncodingEOTF ) const override + { + *displayColorimetry = displaycolorimetry_709; + *displayEOTF = EOTF_Gamma22; + *outputEncodingColorimetry = displaycolorimetry_709; + *outputEncodingEOTF = EOTF_Gamma22; + } + + virtual const char *GetName() const override + { + return "Headless"; + } + virtual const char *GetMake() const override + { + return "Gamescope"; + } + virtual const char *GetModel() const override + { + return "Virtual Display"; + } + + private: + BackendConnectorHDRInfo m_HDRInfo{}; + }; + + class CHeadlessBackend final : public CBaseBackend + { + public: + CHeadlessBackend() + { + } + + virtual ~CHeadlessBackend() + { + } + + virtual bool Init() override + { + g_nOutputWidth = g_nPreferredOutputWidth; + g_nOutputHeight = g_nPreferredOutputHeight; + g_nOutputRefresh = g_nNestedRefresh; + + if ( g_nOutputHeight == 0 ) + { + if ( g_nOutputWidth != 0 ) + { + fprintf( stderr, "Cannot specify -W without -H\n" ); + return false; + } + g_nOutputHeight = 720; + } + if ( g_nOutputWidth == 0 ) + g_nOutputWidth = g_nOutputHeight * 16 / 9; + if ( g_nOutputRefresh == 0 ) + g_nOutputRefresh = ConvertHztomHz( 60 ); + + if ( !vulkan_init( vulkan_get_instance(), VK_NULL_HANDLE ) ) + { + return false; + } + + if ( !wlsession_init() ) + { + fprintf( stderr, "Failed to initialize Wayland session\n" ); + return false; + } + + return true; + } + + virtual bool PostInit() override + { + 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 + { + return VK_IMAGE_LAYOUT_GENERAL; + } + virtual void GetPreferredOutputFormat( uint32_t *pPrimaryPlaneFormat, uint32_t *pOverlayPlaneFormat ) const override + { + *pPrimaryPlaneFormat = VulkanFormatToDRM( VK_FORMAT_A2B10G10R10_UNORM_PACK32, std::nullopt ); + *pOverlayPlaneFormat = VulkanFormatToDRM( VK_FORMAT_B8G8R8A8_UNORM, std::nullopt ); + } + virtual bool ValidPhysicalDevice( VkPhysicalDevice pVkPhysicalDevice ) const override + { + return true; + } + + virtual int Present( const FrameInfo_t *pFrameInfo, bool bAsync ) override + { + return 0; + } + + virtual void DirtyState( bool bForce, bool bForceModeset ) override + { + } + + virtual bool PollState() override + { + return false; + } + + virtual std::shared_ptr CreateBackendBlob( const std::type_info &type, std::span data ) override + { + return std::make_shared( data ); + } + + virtual OwningRc ImportDmabufToBackend( wlr_buffer *pBuffer, wlr_dmabuf_attributes *pDmaBuf ) override + { + return new CBaseBackendFb(); + } + + virtual bool UsesModifiers() const override + { + return false; + } + virtual std::span GetSupportedModifiers( uint32_t uDrmFormat ) const override + { + return std::span{}; + } + + virtual IBackendConnector *GetCurrentConnector() override + { + return &m_Connector; + } + virtual IBackendConnector *GetConnector( GamescopeScreenType eScreenType ) override + { + if ( eScreenType == GAMESCOPE_SCREEN_TYPE_INTERNAL ) + return &m_Connector; + + return nullptr; + } + + virtual bool IsVRRActive() const override + { + return false; + } + + virtual bool SupportsPlaneHardwareCursor() const override + { + return false; + } + + virtual bool SupportsTearing() const override + { + return false; + } + + virtual bool UsesVulkanSwapchain() const override + { + return false; + } + + virtual bool IsSessionBased() const override + { + return false; + } + + virtual bool SupportsExplicitSync() const override + { + return true; + } + + virtual bool IsVisible() const override + { + return true; + } + + virtual glm::uvec2 CursorSurfaceSize( glm::uvec2 uvecSize ) const override + { + return uvecSize; + } + + virtual bool HackTemporarySetDynamicRefresh( int nRefresh ) override + { + return false; + } + + virtual void HackUpdatePatchedEdid() override + { + } + + protected: + + virtual void OnBackendBlobDestroyed( BackendBlob *pBlob ) override + { + } + + private: + + CHeadlessConnector m_Connector; + }; +} \ No newline at end of file diff --git a/src/backend.cpp b/src/backend.cpp index 8a6bbe8ed..c3f5734b0 100644 --- a/src/backend.cpp +++ b/src/backend.cpp @@ -1,4 +1,5 @@ #include "backend.h" +#include "Backends/HeadlessBackend.hpp" #include "vblankmanager.hpp" #include "convar.h" #include "wlserver.hpp" @@ -16,34 +17,61 @@ namespace gamescope // IBackend ///////////// - static IBackend *s_pBackend = nullptr; + static std::atomic s_pBackend = nullptr; IBackend *IBackend::Get() { return s_pBackend; } + + static void ReplaceBackend(IBackend* pNewBackend); + bool IBackend::Set( IBackend *pBackend ) { if ( s_pBackend ) { - delete s_pBackend; - s_pBackend = nullptr; + GetBackend()->DestroyBackend(); //we're intentionally *not* setting s_pBackend to nullptr after deletion. + //the DestroyBackend() method ensures that + //IBackend::Get will still point to a safe memory region (a CHeadlessBackend object) after it is run } if ( pBackend ) { - s_pBackend = pBackend; - if ( !s_pBackend->Init() ) + ReplaceBackend(pBackend); + if ( !GetBackend()->Init() ) { - delete s_pBackend; - s_pBackend = nullptr; + pBackend->DestroyBackend(); return false; } } return true; } + + static inline IBackend *s_pOldBackend = nullptr; //for internal book-keeping only + + void IBackend::DestroyBackend() { + assert(s_pOldBackend == nullptr); + s_pBackend = new CHeadlessBackend{}; + s_pOldBackend = this; + this->~IBackend(); //manually call the backend's destructor, without deallocating the memory + } + + static void ReplaceBackend(IBackend* pNewBackend) + { + if (s_pBackend) { + ::operator delete(s_pBackend); + } + + s_pBackend = pNewBackend; + if (auto* pOld = std::exchange(s_pOldBackend, nullptr); + pOld != nullptr) + { //delete the old backend for realsies + ::operator delete(pOld); + } + } + ///////////////// // CBaseBackendFb @@ -184,4 +212,4 @@ namespace gamescope GetBackend()->DumpDebugInfo(); }); -} +} \ No newline at end of file diff --git a/src/backend.h b/src/backend.h index dd295f4ac..8020e0ced 100644 --- a/src/backend.h +++ b/src/backend.h @@ -170,7 +170,7 @@ namespace gamescope wlr_buffer *m_pClientBuffer = nullptr; std::shared_ptr m_pReleasePoint; }; - + class IBackend { public: @@ -206,6 +206,7 @@ namespace gamescope virtual bool UsesModifiers() const = 0; virtual std::span GetSupportedModifiers( uint32_t uDrmFormat ) const = 0; + inline bool SupportsFormat( uint32_t uDrmFormat ) const { return Algorithm::Contains( this->GetSupportedModifiers( uDrmFormat ), DRM_FORMAT_MOD_INVALID ); @@ -263,6 +264,8 @@ namespace gamescope virtual void OnBackendBlobDestroyed( BackendBlob *pBlob ) = 0; private: + + void DestroyBackend(); }; @@ -347,5 +350,4 @@ namespace gamescope inline gamescope::IBackend *GetBackend() { return gamescope::IBackend::Get(); -} - +} \ No newline at end of file diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 11a7cade6..8d15bda9d 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -5896,7 +5896,7 @@ error(Display *dpy, XErrorEvent *ev) } [[noreturn]] static void -steamcompmgr_exit(void) +steamcompmgr_exit(std::optional> lock = std::nullopt) { g_ImageWaiter.Shutdown(); @@ -5936,7 +5936,9 @@ steamcompmgr_exit(void) wlserver_lock(); wlserver_shutdown(); wlserver_unlock(false); - + + if (lock) + lock->unlock(); pthread_exit(NULL); } @@ -8042,7 +8044,7 @@ steamcompmgr_main(int argc, char **argv) vblank = false; } - steamcompmgr_exit(); + steamcompmgr_exit(std::optional> {std::in_place_t{}, std::move(xwayland_server_guard)} ); } void steamcompmgr_send_frame_done_to_focus_window()