diff --git a/src/managers/FrameSchedulingManager.cpp b/src/managers/FrameSchedulingManager.cpp index 2c650913..f2ba1018 100644 --- a/src/managers/FrameSchedulingManager.cpp +++ b/src/managers/FrameSchedulingManager.cpp @@ -1,9 +1,29 @@ #include "FrameSchedulingManager.hpp" #include "../debug/Log.hpp" #include "../Compositor.hpp" +#include "eventLoop/EventLoopManager.hpp" -int onPresentTimer(void* data) { - return g_pFrameSchedulingManager->onVblankTimer(data); +static void onPresentTimer(std::shared_ptr self, void* data) { + g_pFrameSchedulingManager->onVblankTimer(data); +} + +static void onFenceTimer(std::shared_ptr self, void* data) { + g_pFrameSchedulingManager->onFenceTimer((CMonitor*)data); +} + +void CFrameSchedulingManager::onFenceTimer(CMonitor* pMonitor) { + SSchedulingData* DATA = &m_vSchedulingData.emplace_back(SSchedulingData{pMonitor}); + + RASSERT(DATA, "No data in fenceTimer"); + +#ifndef GLES2 + GLint syncStatus = 0; + glGetSynciv(DATA->fenceSync, GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); + bool GPUSignaled = syncStatus == GL_SIGNALED; + + if (GPUSignaled) + gpuDone(pMonitor); +#endif } void CFrameSchedulingManager::registerMonitor(CMonitor* pMonitor) { @@ -13,12 +33,22 @@ void CFrameSchedulingManager::registerMonitor(CMonitor* pMonitor) { } SSchedulingData* DATA = &m_vSchedulingData.emplace_back(SSchedulingData{pMonitor}); - DATA->event = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, onPresentTimer, DATA); +#ifdef GLES2 + DATA->legacyScheduler = true; +#else DATA->legacyScheduler = !wlr_backend_is_drm(pMonitor->output->backend); +#endif + + DATA->fenceTimer = std::make_shared(::onFenceTimer, pMonitor); + DATA->vblankTimer = std::make_shared(::onPresentTimer, pMonitor); + + g_pEventLoopManager->addTimer(DATA->fenceTimer); } void CFrameSchedulingManager::unregisterMonitor(CMonitor* pMonitor) { + SSchedulingData* DATA = &m_vSchedulingData.emplace_back(SSchedulingData{pMonitor}); + g_pEventLoopManager->removeTimer(DATA->fenceTimer); std::erase_if(m_vSchedulingData, [pMonitor](const auto& d) { return d.pMonitor == pMonitor; }); } @@ -41,8 +71,8 @@ void CFrameSchedulingManager::onFrameNeeded(CMonitor* pMonitor) { onPresent(pMonitor, nullptr); } -void CFrameSchedulingManager::gpuDone(wlr_buffer* pBuffer) { - const auto DATA = dataFor(pBuffer); +void CFrameSchedulingManager::gpuDone(CMonitor* pMonitor) { + const auto DATA = dataFor(pMonitor); Debug::log(LOG, "gpuDone"); @@ -119,7 +149,6 @@ void CFrameSchedulingManager::onPresent(CMonitor* pMonitor, wlr_output_event_pre if (DATA->forceFrames > 0) DATA->forceFrames--; DATA->rendered = false; - DATA->gpuReady = false; DATA->activelyPushing = true; // check if there is damage @@ -138,7 +167,7 @@ void CFrameSchedulingManager::onPresent(CMonitor* pMonitor, wlr_output_event_pre Debug::log(LOG, "render"); // we can't do this on wayland - float msUntilVblank = 0; + float µsUntilVblank = 0; if (presentationData) { timespec now; @@ -147,22 +176,24 @@ void CFrameSchedulingManager::onPresent(CMonitor* pMonitor, wlr_output_event_pre std::chrono::duration_cast(std::chrono::seconds{now.tv_sec} + std::chrono::nanoseconds{now.tv_nsec}) - std::chrono::duration_cast(std::chrono::seconds{presentationData->when->tv_sec} + std::chrono::nanoseconds{presentationData->when->tv_nsec})}; - msUntilVblank = (presentationData->refresh ? presentationData->refresh / 1000000.0 : pMonitor->refreshRate / 1000.0) - - std::chrono::duration_cast(LASTVBLANK).count(); + µsUntilVblank = (presentationData->refresh ? presentationData->refresh / 1000000.0 : pMonitor->refreshRate / 1000.0) - + std::chrono::duration_cast(LASTVBLANK).count(); DATA->nextVblank = std::chrono::system_clock::now() + LASTVBLANK + std::chrono::nanoseconds{int{presentationData->refresh ? presentationData->refresh : 1000000000 / pMonitor->refreshRate}}; } else - msUntilVblank = std::chrono::duration_cast(DATA->nextVblank - std::chrono::system_clock::now()).count(); + µsUntilVblank = std::chrono::duration_cast(DATA->nextVblank - std::chrono::system_clock::now()).count(); - if (msUntilVblank > 0) { - wl_event_source_timer_update(DATA->event, 0); - wl_event_source_timer_update(DATA->event, std::floor(msUntilVblank + 1)); - } + if (µsUntilVblank > 10) + DATA->vblankTimer->updateTimeout(std::chrono::microseconds((long)(µsUntilVblank - 100))); - Debug::log(LOG, "until vblank {:.2f}", msUntilVblank); + Debug::log(LOG, "until vblank {:.2f}µs", µsUntilVblank); renderMonitor(DATA); + +#ifndef GLES2 + DATA->fenceSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); +#endif } CFrameSchedulingManager::SSchedulingData* CFrameSchedulingManager::dataFor(CMonitor* pMonitor) { @@ -219,25 +250,33 @@ void CFrameSchedulingManager::renderMonitor(SSchedulingData* data) { data->rendered = true; } -int CFrameSchedulingManager::onVblankTimer(void* data) { +void CFrameSchedulingManager::onVblankTimer(void* data) { auto DATA = (SSchedulingData*)data; - if (DATA->rendered && DATA->gpuReady) { +#ifndef GLES2 + + GLint syncStatus = 0; + glGetSynciv(DATA->fenceSync, GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); + bool GPUSignaled = syncStatus == GL_SIGNALED; + + if (DATA->rendered && GPUSignaled) { Debug::log(LOG, "timer nothing"); // cool, we don't need to do anything. Wait for present. - return 0; + return; } - if (DATA->rendered && !DATA->gpuReady) { + if (DATA->rendered && !GPUSignaled) { Debug::log(LOG, "timer delay"); // we missed a vblank :( DATA->delayed = true; - return 0; + // start the fence timer + DATA->fenceTimer->updateTimeout(std::chrono::microseconds(850)); + return; } // what the fuck? Debug::log(ERR, "Vblank timer fired without a frame????"); - return 0; +#endif } bool CFrameSchedulingManager::isMonitorUsingLegacyScheduler(CMonitor* pMonitor) { diff --git a/src/managers/FrameSchedulingManager.hpp b/src/managers/FrameSchedulingManager.hpp index 0ab356ee..60b277b7 100644 --- a/src/managers/FrameSchedulingManager.hpp +++ b/src/managers/FrameSchedulingManager.hpp @@ -3,6 +3,12 @@ #include #include #include "../helpers/Timer.hpp" +#include "eventLoop/EventLoopTimer.hpp" +#ifndef GLES2 +#include +#else +#define GLsync void* +#endif class CMonitor; struct wlr_buffer; @@ -13,16 +19,17 @@ class CFrameSchedulingManager { void registerMonitor(CMonitor* pMonitor); void unregisterMonitor(CMonitor* pMonitor); - void gpuDone(wlr_buffer* pBuffer); + void gpuDone(CMonitor* pMonitor); void registerBuffer(wlr_buffer* pBuffer, CMonitor* pMonitor); void dropBuffer(wlr_buffer* pBuffer); + void onFenceTimer(CMonitor* pMonitor); void onFrameNeeded(CMonitor* pMonitor); void onPresent(CMonitor* pMonitor, wlr_output_event_present* presentationData); void onFrame(CMonitor* pMonitor); - int onVblankTimer(void* data); + void onVblankTimer(void* data); bool isMonitorUsingLegacyScheduler(CMonitor* pMonitor); @@ -33,9 +40,6 @@ class CFrameSchedulingManager { // CPU frame rendering has been finished bool rendered = false; - // GPU frame rendering has been finished - bool gpuReady = false; - // GPU didn't manage to render last frame in time. // we got a vblank before we got a gpuDone() bool delayed = false; @@ -52,9 +56,6 @@ class CFrameSchedulingManager { // buffers associated with this monitor std::vector buffers; - // event source for the vblank timer - wl_event_source* event = nullptr; - // whether we're actively pushing frames bool activelyPushing = false; @@ -64,6 +65,13 @@ class CFrameSchedulingManager { // next predicted vblank std::chrono::system_clock::time_point nextVblank; + + // for delayed fence stuff + std::shared_ptr fenceTimer; + std::shared_ptr vblankTimer; + + // fence sync + GLsync fenceSync = 0; }; std::vector m_vSchedulingData; diff --git a/src/managers/eventLoop/EventLoopTimer.cpp b/src/managers/eventLoop/EventLoopTimer.cpp index 5ece1cac..78c1cb15 100644 --- a/src/managers/eventLoop/EventLoopTimer.cpp +++ b/src/managers/eventLoop/EventLoopTimer.cpp @@ -13,6 +13,13 @@ CEventLoopTimer::CEventLoopTimer(std::optional self, void* data)> cb_, + void* data_) : + cb(cb_), + data(data_) { + ; +} + void CEventLoopTimer::updateTimeout(std::optional timeout) { if (!timeout.has_value()) { expires.reset(); diff --git a/src/managers/eventLoop/EventLoopTimer.hpp b/src/managers/eventLoop/EventLoopTimer.hpp index 8865d29f..f273defa 100644 --- a/src/managers/eventLoop/EventLoopTimer.hpp +++ b/src/managers/eventLoop/EventLoopTimer.hpp @@ -7,6 +7,7 @@ class CEventLoopTimer { public: CEventLoopTimer(std::optional timeout, std::function self, void* data)> cb_, void* data_); + CEventLoopTimer(std::function self, void* data)> cb_, void* data_); // if not specified, disarms. // if specified, arms. diff --git a/src/render/Renderbuffer.cpp b/src/render/Renderbuffer.cpp index 8935d83e..eec81189 100644 --- a/src/render/Renderbuffer.cpp +++ b/src/render/Renderbuffer.cpp @@ -22,20 +22,6 @@ CRenderbuffer::~CRenderbuffer() { g_pFrameSchedulingManager->dropBuffer(m_pWlrBuffer); } -static int fdHandleWrite(int fd, uint32_t mask, void* data) { - if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP) - return 0; - - const auto RB = (CRenderbuffer*)data; - - if (RB->hasFence()) - g_pFrameSchedulingManager->gpuDone(RB->m_pWlrBuffer); - - RB->removeFence(); - - return 0; -} - CRenderbuffer::CRenderbuffer(wlr_buffer* buffer, uint32_t format, CMonitor* pMonitor) : m_pWlrBuffer(buffer), m_pMonitor(pMonitor) { // EVIL, but we can't include a hidden header because nixos is fucking special @@ -109,19 +95,3 @@ void CRenderbuffer::unbind() { CFramebuffer* CRenderbuffer::getFB() { return &m_sFramebuffer; } - -void CRenderbuffer::plantFence() { - wlr_dmabuf_attributes attrs = {0}; - wlr_buffer_get_dmabuf(m_pWlrBuffer, &attrs); - m_pFDWrite = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, attrs.fd[0], WL_EVENT_WRITABLE, fdHandleWrite, this); -} - -void CRenderbuffer::removeFence() { - if (m_pFDWrite) - wl_event_source_remove(m_pFDWrite); - m_pFDWrite = nullptr; -} - -bool CRenderbuffer::hasFence() { - return m_pFDWrite; -} diff --git a/src/render/Renderbuffer.hpp b/src/render/Renderbuffer.hpp index 668a89a5..ecfb7b3c 100644 --- a/src/render/Renderbuffer.hpp +++ b/src/render/Renderbuffer.hpp @@ -13,9 +13,6 @@ class CRenderbuffer { void bindFB(); void unbind(); CFramebuffer* getFB(); - void plantFence(); - void removeFence(); - bool hasFence(); wlr_buffer* m_pWlrBuffer = nullptr; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 69970149..fe0ae918 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -2676,7 +2676,6 @@ void CHyprRenderer::endRender() { glFlush(); if (m_eRenderMode == RENDER_MODE_NORMAL) { - m_pCurrentRenderbuffer->plantFence(); wlr_output_state_set_buffer(PMONITOR->state.wlr(), m_pCurrentWlrBuffer); unsetEGL(); // flush the context }