mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-07 18:45:59 +01:00
use gl sync
This commit is contained in:
parent
db3259bfe4
commit
c53fcd0c80
7 changed files with 84 additions and 63 deletions
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -13,9 +13,6 @@ class CRenderbuffer {
|
|||
void bindFB();
|
||||
void unbind();
|
||||
CFramebuffer* getFB();
|
||||
void plantFence();
|
||||
void removeFence();
|
||||
bool hasFence();
|
||||
|
||||
wlr_buffer* m_pWlrBuffer = nullptr;
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue