use gl sync

This commit is contained in:
Vaxry 2024-04-08 16:08:43 +01:00
parent db3259bfe4
commit c53fcd0c80
7 changed files with 84 additions and 63 deletions

View File

@ -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<CEventLoopTimer> self, void* data) {
g_pFrameSchedulingManager->onVblankTimer(data);
}
static void onFenceTimer(std::shared_ptr<CEventLoopTimer> 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<CEventLoopTimer>(::onFenceTimer, pMonitor);
DATA->vblankTimer = std::make_shared<CEventLoopTimer>(::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::system_clock::duration>(std::chrono::seconds{now.tv_sec} + std::chrono::nanoseconds{now.tv_nsec}) -
std::chrono::duration_cast<std::chrono::system_clock::duration>(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<std::chrono::milliseconds>(LASTVBLANK).count();
µsUntilVblank = (presentationData->refresh ? presentationData->refresh / 1000000.0 : pMonitor->refreshRate / 1000.0) -
std::chrono::duration_cast<std::chrono::microseconds>(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<std::chrono::milliseconds>(DATA->nextVblank - std::chrono::system_clock::now()).count();
µsUntilVblank = std::chrono::duration_cast<std::chrono::microseconds>(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) {

View File

@ -3,6 +3,12 @@
#include <memory>
#include <vector>
#include "../helpers/Timer.hpp"
#include "eventLoop/EventLoopTimer.hpp"
#ifndef GLES2
#include <GLES3/gl32.h>
#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<wlr_buffer*> 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<CEventLoopTimer> fenceTimer;
std::shared_ptr<CEventLoopTimer> vblankTimer;
// fence sync
GLsync fenceSync = 0;
};
std::vector<SSchedulingData> m_vSchedulingData;

View File

@ -13,6 +13,13 @@ CEventLoopTimer::CEventLoopTimer(std::optional<std::chrono::system_clock::durati
expires = std::chrono::system_clock::now() + *timeout;
}
CEventLoopTimer::CEventLoopTimer(std::function<void(std::shared_ptr<CEventLoopTimer> self, void* data)> cb_,
void* data_) :
cb(cb_),
data(data_) {
;
}
void CEventLoopTimer::updateTimeout(std::optional<std::chrono::system_clock::duration> timeout) {
if (!timeout.has_value()) {
expires.reset();

View File

@ -7,6 +7,7 @@
class CEventLoopTimer {
public:
CEventLoopTimer(std::optional<std::chrono::system_clock::duration> timeout, std::function<void(std::shared_ptr<CEventLoopTimer> self, void* data)> cb_, void* data_);
CEventLoopTimer(std::function<void(std::shared_ptr<CEventLoopTimer> self, void* data)> cb_, void* data_);
// if not specified, disarms.
// if specified, arms.

View File

@ -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;
}

View File

@ -13,9 +13,6 @@ class CRenderbuffer {
void bindFB();
void unbind();
CFramebuffer* getFB();
void plantFence();
void removeFence();
bool hasFence();
wlr_buffer* m_pWlrBuffer = nullptr;

View File

@ -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
}