From 12af8413417d5ad693348ee5b140a426471b0be6 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Thu, 2 May 2024 16:01:58 +0100 Subject: [PATCH] screencopy: fixup timing issues with frame re-scheduling ref #120 --- src/core/PortalManager.cpp | 6 +++--- src/core/PortalManager.hpp | 7 ++++--- src/portals/Screencopy.cpp | 16 +++++++++++++++- src/portals/Screencopy.hpp | 22 ++++++++++++---------- 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/core/PortalManager.cpp b/src/core/PortalManager.cpp index 584a974..7d7835c 100644 --- a/src/core/PortalManager.cpp +++ b/src/core/PortalManager.cpp @@ -41,7 +41,8 @@ static void handleOutputDone(void* data, struct wl_output* wl_output) { } static void handleOutputMode(void* data, struct wl_output* wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { - ; + const auto POUTPUT = (SOutput*)data; + POUTPUT->refreshRate = std::round(refresh / 1000.0); } static void handleOutputScale(void* data, struct wl_output* wl_output, int32_t factor) { @@ -422,7 +423,7 @@ void CPortalManager::startEventLoop() { // wait for being awakened std::unique_lock lk(m_sEventLoopInternals.loopMutex); if (m_sEventLoopInternals.shouldProcess == false) // avoid a lock if a thread managed to request something already since we .unlock()ed - m_sEventLoopInternals.loopSignal.wait(lk, [this] { return m_sEventLoopInternals.shouldProcess == true; }); // wait for events + m_sEventLoopInternals.loopSignal.wait_for(lk, std::chrono::seconds(5), [this] { return m_sEventLoopInternals.shouldProcess == true; }); // wait for events std::lock_guard lg(m_sEventLoopInternals.loopRequestMutex); @@ -464,7 +465,6 @@ void CPortalManager::startEventLoop() { } } - // finalize wayland dispatching. Dispatch pending on the queue int ret = 0; do { ret = wl_display_dispatch_pending(m_sWaylandConnection.display); diff --git a/src/core/PortalManager.hpp b/src/core/PortalManager.hpp index b6cbbfa..6907a33 100644 --- a/src/core/PortalManager.hpp +++ b/src/core/PortalManager.hpp @@ -19,9 +19,10 @@ struct pw_loop; struct SOutput { std::string name; - wl_output* output = nullptr; - uint32_t id = 0; - wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + wl_output* output = nullptr; + uint32_t id = 0; + float refreshRate = 60.0; + wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; }; struct SDMABUFModifier { diff --git a/src/portals/Screencopy.cpp b/src/portals/Screencopy.cpp index 36b8171..465011b 100644 --- a/src/portals/Screencopy.cpp +++ b/src/portals/Screencopy.cpp @@ -488,6 +488,11 @@ void CScreencopyPortal::onSelectSources(sdbus::MethodCall& call) { if (SHAREDATA.type == TYPE_WINDOW && !m_sState.toplevel) { Debug::log(ERR, "[screencopy] Requested type window for no toplevel export protocol!"); SHAREDATA.type = TYPE_INVALID; + } else if (SHAREDATA.type == TYPE_OUTPUT || SHAREDATA.type == TYPE_GEOMETRY) { + const auto POUTPUT = g_pPortalManager->getOutputFromName(SHAREDATA.output); + + if (POUTPUT) + PSESSION->sharingData.framerate = POUTPUT->refreshRate; } PSESSION->selection = SHAREDATA; @@ -670,7 +675,15 @@ void CScreencopyPortal::queueNextShareFrame(CScreencopyPortal::SSession* pSessio if (PSTREAM && !PSTREAM->streamState) return; - g_pPortalManager->addTimer({1000.0 / pSession->sharingData.framerate, [pSession]() { g_pPortalManager->m_sPortals.screencopy->startFrameCopy(pSession); }}); + // calculate frame delta and queue next frame + const auto FRAMETOOKMS = std::chrono::duration_cast(std::chrono::system_clock::now() - pSession->sharingData.begunFrame).count() / 1000.0; + const auto MSTILNEXTREFRESH = 1000.0 / (pSession->sharingData.framerate) - FRAMETOOKMS; + pSession->sharingData.begunFrame = std::chrono::system_clock::now(); + + Debug::log(TRACE, "[screencopy] set fps {}, ms till next refresh {:.2f}, estimated actual fps: {:.2f}", pSession->sharingData.framerate, MSTILNEXTREFRESH, + std::clamp(1000.0 / FRAMETOOKMS, 1.0, (double)pSession->sharingData.framerate)); + + g_pPortalManager->addTimer({std::clamp(MSTILNEXTREFRESH, 1.0, 1000.0), [pSession]() { g_pPortalManager->m_sPortals.screencopy->startFrameCopy(pSession); }}); } bool CScreencopyPortal::hasToplevelCapabilities() { return m_sState.toplevel; @@ -804,6 +817,7 @@ static void pwStreamParamChanged(void* data, uint32_t id, const spa_pod* param) spa_pod_dynamic_builder_init(&dynBuilder[2], params_buffer[2], sizeof(params_buffer[2]), 2048); spa_format_video_raw_parse(param, &PSTREAM->pwVideoInfo); + Debug::log(TRACE, "[pw] Framerate: {}/{}", PSTREAM->pwVideoInfo.max_framerate.num, PSTREAM->pwVideoInfo.max_framerate.denom); PSTREAM->pSession->sharingData.framerate = PSTREAM->pwVideoInfo.max_framerate.num / PSTREAM->pwVideoInfo.max_framerate.denom; uint32_t data_type = 1 << SPA_DATA_MemFd; diff --git a/src/portals/Screencopy.hpp b/src/portals/Screencopy.hpp index da3bb6c..1396ecb 100644 --- a/src/portals/Screencopy.hpp +++ b/src/portals/Screencopy.hpp @@ -6,6 +6,7 @@ #include "../shared/ScreencopyShared.hpp" #include #include "../shared/Session.hpp" +#include enum cursorModes { HIDDEN = 1, @@ -69,16 +70,17 @@ class CScreencopyPortal { SSelectionData selection; struct { - bool active = false; - zwlr_screencopy_frame_v1* frameCallback = nullptr; - hyprland_toplevel_export_frame_v1* windowFrameCallback = nullptr; - frameStatus status = FRAME_NONE; - uint64_t tvSec = 0; - uint32_t tvNsec = 0; - uint64_t tvTimestampNs = 0; - uint32_t nodeID = 0; - uint32_t framerate = 60; - wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + bool active = false; + zwlr_screencopy_frame_v1* frameCallback = nullptr; + hyprland_toplevel_export_frame_v1* windowFrameCallback = nullptr; + frameStatus status = FRAME_NONE; + uint64_t tvSec = 0; + uint32_t tvNsec = 0; + uint64_t tvTimestampNs = 0; + uint32_t nodeID = 0; + uint32_t framerate = 60; + wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + std::chrono::system_clock::time_point begunFrame = std::chrono::system_clock::now(); struct { uint32_t w = 0, h = 0, size = 0, stride = 0, fmt = 0;