Remove vsync thread from X11 backend

This commit is contained in:
Jack Andersen 2019-02-11 21:18:35 -10:00
parent 3e1da36f39
commit 45db327fb3
6 changed files with 137 additions and 132 deletions

View File

@ -251,6 +251,7 @@ public:
virtual void setCursor(EMouseCursor cursor) = 0;
virtual void setWaitCursor(bool wait) = 0;
virtual double getWindowRefreshRate() const = 0;
virtual void setWindowFrameDefault() = 0;
virtual void getWindowFrame(float& xOut, float& yOut, float& wOut, float& hOut) const = 0;
virtual void getWindowFrame(int& xOut, int& yOut, int& wOut, int& hOut) const = 0;
@ -273,7 +274,7 @@ public:
virtual bool clipboardCopy(EClipboardType type, const uint8_t* data, size_t sz) = 0;
virtual std::unique_ptr<uint8_t[]> clipboardPaste(EClipboardType type, size_t& sz) = 0;
virtual void waitForRetrace() = 0;
virtual int waitForRetrace() = 0;
virtual uintptr_t getPlatformHandle() const = 0;
virtual bool _incomingEvent(void* event) {

View File

@ -149,9 +149,10 @@ public:
std::recursive_mutex m_callbackMutex;
IWindowCallback* m_callback = nullptr;
void waitForRetrace() {
int waitForRetrace() {
std::unique_lock<std::mutex> lk(m_dlmt);
m_dlcv.wait(lk);
return 1;
}
virtual BooCocoaResponder* responder() const = 0;
@ -1321,6 +1322,11 @@ public:
void setWaitCursor(bool wait) {}
double getWindowRefreshRate() const {
/* TODO: Actually get refresh rate */
return 60.0;
}
void setWindowFrameDefault() {
dispatch_sync(dispatch_get_main_queue(),
^{
@ -1456,8 +1462,8 @@ public:
});
}
void waitForRetrace() {
static_cast<GraphicsContextCocoa*>(m_gfxCtx)->waitForRetrace();
int waitForRetrace() {
return static_cast<GraphicsContextCocoa*>(m_gfxCtx)->waitForRetrace();
}
uintptr_t getPlatformHandle() const {

View File

@ -322,6 +322,11 @@ public:
void setWaitCursor(bool wait) {}
double getWindowRefreshRate() const {
/* TODO: Actually get refresh rate */
return 60.0;
}
void setWindowFrameDefault() {}
void getWindowFrame(float& xOut, float& yOut, float& wOut, float& hOut) const {
@ -359,10 +364,11 @@ public:
std::unique_ptr<uint8_t[]> clipboardPaste(EClipboardType type, size_t& sz) { return std::unique_ptr<uint8_t[]>(); }
void waitForRetrace(IAudioVoiceEngine* engine) {
int waitForRetrace(IAudioVoiceEngine* engine) {
if (engine)
engine->pumpAndMixVoices();
m_gfxCtx->m_output->WaitForVBlank();
return 1;
}
uintptr_t getPlatformHandle() const { return 0; }

View File

@ -914,6 +914,11 @@ public:
}
}
double getWindowRefreshRate() const {
/* TODO: Actually get refresh rate */
return 60.0;
}
void setWindowFrameDefault() {
MONITORINFO monInfo = {};
monInfo.cbSize = sizeof(MONITORINFO);
@ -1063,7 +1068,7 @@ public:
return std::unique_ptr<uint8_t[]>();
}
void waitForRetrace() { m_gfxCtx->m_output->WaitForVBlank(); }
int waitForRetrace() { m_gfxCtx->m_output->WaitForVBlank(); return 1; }
uintptr_t getPlatformHandle() const { return uintptr_t(m_hwnd); }

View File

@ -73,6 +73,8 @@ struct WindowWayland : IWindow {
void setWaitCursor(bool wait) {}
double getWindowRefreshRate() const { return 60.0; }
void setWindowFrameDefault() {}
void getWindowFrame(float& xOut, float& yOut, float& wOut, float& hOut) const {}
@ -99,7 +101,7 @@ struct WindowWayland : IWindow {
std::unique_ptr<uint8_t[]> clipboardPaste(EClipboardType type, size_t& sz) { return std::unique_ptr<uint8_t[]>(); }
void waitForRetrace() {}
int waitForRetrace() { return 1; }
uintptr_t getPlatformHandle() const { return 0; }

View File

@ -11,6 +11,7 @@
#endif
#include <limits.h>
#include <unistd.h>
#include <cstdlib>
#include <cstdio>
#include <cstdint>
@ -57,8 +58,6 @@
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
static glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
typedef int (*glXWaitVideoSyncSGIProc)(int divisor, int remainder, unsigned int* count);
static glXWaitVideoSyncSGIProc glXWaitVideoSyncSGI = 0;
static bool s_glxError;
static int ctxErrorHandler(Display* dpy, XErrorEvent* ev) {
s_glxError = true;
@ -262,11 +261,6 @@ struct GraphicsContextXlib : IGraphicsContext {
GLContext* m_glCtx;
Display* m_xDisp;
std::mutex m_initmt;
std::condition_variable m_initcv;
std::mutex m_vsyncmt;
std::condition_variable m_vsynccv;
GraphicsContextXlib(EGraphicsAPI api, EPixelFormat pf, IWindow* parentWindow, Display* disp, GLContext* glCtx)
: m_api(api), m_pf(pf), m_parentWindow(parentWindow), m_glCtx(glCtx), m_xDisp(disp) {}
virtual void destroy() = 0;
@ -287,9 +281,6 @@ struct GraphicsContextXlibGLX : GraphicsContextXlib {
GLXContext m_mainCtx = 0;
GLXContext m_loadCtx = 0;
std::thread m_vsyncThread;
bool m_vsyncRunning;
public:
IWindowCallback* m_callback;
@ -371,10 +362,6 @@ public:
glXDestroyContext(m_xDisp, m_loadCtx);
m_loadCtx = nullptr;
}
if (m_vsyncRunning) {
m_vsyncRunning = false;
m_vsyncThread.join();
}
}
~GraphicsContextXlibGLX() { destroy(); }
@ -400,11 +387,6 @@ public:
if (!glXCreateContextAttribsARB)
Log.report(logvisor::Fatal, "unable to resolve glXCreateContextAttribsARB");
}
if (!glXWaitVideoSyncSGI) {
glXWaitVideoSyncSGI = (glXWaitVideoSyncSGIProc)glXGetProcAddressARB((const GLubyte*)"glXWaitVideoSyncSGI");
if (!glXWaitVideoSyncSGI)
Log.report(logvisor::Fatal, "unable to resolve glXWaitVideoSyncSGI");
}
s_glxError = false;
XErrorHandler oldHandler = XSetErrorHandler(ctxErrorHandler);
@ -427,50 +409,6 @@ public:
Log.report(logvisor::Fatal, "glewInit failed");
glXMakeCurrent(m_xDisp, 0, 0);
/* Spawn vsync thread */
m_vsyncRunning = true;
std::unique_lock<std::mutex> outerLk(m_initmt);
m_vsyncThread = std::thread([&]() {
Display* vsyncDisp;
GLXContext vsyncCtx;
{
std::unique_lock<std::mutex> innerLk(m_initmt);
vsyncDisp = XOpenDisplay(0);
if (!vsyncDisp)
Log.report(logvisor::Fatal, "unable to open new vsync display");
XLockDisplay(vsyncDisp);
static int attributeList[] = {GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1,
0};
XVisualInfo* vi = glXChooseVisual(vsyncDisp, DefaultScreen(vsyncDisp), attributeList);
vsyncCtx = glXCreateContext(vsyncDisp, vi, nullptr, True);
if (!vsyncCtx)
Log.report(logvisor::Fatal, "unable to make new vsync GLX context");
if (!glXMakeCurrent(vsyncDisp, DefaultRootWindow(vsyncDisp), vsyncCtx))
Log.report(logvisor::Fatal, "unable to make vsync context current");
}
m_initcv.notify_one();
while (m_vsyncRunning) {
{
unsigned int sync;
int err = glXWaitVideoSyncSGI(1, 0, &sync);
if (err)
Log.report(logvisor::Fatal, "wait err");
}
m_vsynccv.notify_one();
}
glXMakeCurrent(vsyncDisp, 0, nullptr);
glXDestroyContext(vsyncDisp, vsyncCtx);
XUnlockDisplay(vsyncDisp);
XCloseDisplay(vsyncDisp);
});
m_initcv.wait(outerLk);
XUnlockDisplay(m_xDisp);
m_commandQueue = _NewGLCommandQueue(this, m_glCtx);
m_commandQueue->startRenderer();
@ -546,9 +484,6 @@ struct GraphicsContextXlibVulkan : GraphicsContextXlib {
std::unique_ptr<IGraphicsDataFactory> m_dataFactory;
std::unique_ptr<IGraphicsCommandQueue> m_commandQueue;
std::thread m_vsyncThread;
std::atomic_bool m_vsyncRunning;
static void ThrowIfFailed(VkResult res) {
if (res != VK_SUCCESS)
Log.report(logvisor::Fatal, "%d\n", res);
@ -576,12 +511,6 @@ public:
vk::DestroySurfaceKHR(m_ctx->m_instance, m_surface, nullptr);
m_surface = VK_NULL_HANDLE;
}
if (m_vsyncRunning.load()) {
m_vsyncRunning.store(false);
if (m_vsyncThread.joinable())
m_vsyncThread.join();
}
}
~GraphicsContextXlibVulkan() { destroy(); }
@ -606,12 +535,6 @@ public:
}
bool initializeContext(void* getVkProc) {
if (!glXWaitVideoSyncSGI) {
glXWaitVideoSyncSGI = (glXWaitVideoSyncSGIProc)glXGetProcAddressARB((const GLubyte*)"glXWaitVideoSyncSGI");
if (!glXWaitVideoSyncSGI)
Log.report(logvisor::Fatal, "unable to resolve glXWaitVideoSyncSGI");
}
if (m_ctx->m_instance == VK_NULL_HANDLE)
m_ctx->initVulkan(APP->getUniqueName(), PFN_vkGetInstanceProcAddr(getVkProc));
@ -700,49 +623,6 @@ public:
m_ctx->initSwapChain(*m_windowCtx, m_surface, m_format, m_colorspace);
/* Spawn vsync thread */
m_vsyncRunning.store(true);
std::unique_lock<std::mutex> outerLk(m_initmt);
m_vsyncThread = std::thread([&]() {
logvisor::RegisterThreadName("Boo VSync");
Display* vsyncDisp;
GLXContext vsyncCtx;
{
std::unique_lock<std::mutex> innerLk(m_initmt);
vsyncDisp = XOpenDisplay(0);
if (!vsyncDisp)
Log.report(logvisor::Fatal, "unable to open new vsync display");
XLockDisplay(vsyncDisp);
static int attributeList[] = {GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1,
0};
XVisualInfo* vi = glXChooseVisual(vsyncDisp, DefaultScreen(vsyncDisp), attributeList);
vsyncCtx = glXCreateContext(vsyncDisp, vi, nullptr, True);
if (!vsyncCtx)
Log.report(logvisor::Fatal, "unable to make new vsync GLX context");
if (!glXMakeCurrent(vsyncDisp, DefaultRootWindow(vsyncDisp), vsyncCtx))
Log.report(logvisor::Fatal, "unable to make vsync context current");
}
m_initcv.notify_one();
while (m_vsyncRunning.load()) {
unsigned int sync;
int err = glXWaitVideoSyncSGI(1, 0, &sync);
if (err)
Log.report(logvisor::Fatal, "wait err");
m_vsynccv.notify_one();
}
glXMakeCurrent(vsyncDisp, 0, nullptr);
glXDestroyContext(vsyncDisp, vsyncCtx);
XUnlockDisplay(vsyncDisp);
XCloseDisplay(vsyncDisp);
});
m_initcv.wait(outerLk);
m_dataFactory = _NewVulkanDataFactory(this, m_ctx);
m_commandQueue = _NewVulkanCommandQueue(m_ctx, m_ctx->m_windows[m_parentWindow].get(), this);
m_commandQueue->startRenderer();
@ -766,7 +646,7 @@ public:
};
#endif
class WindowXlib : public IWindow {
class WindowXlib final : public IWindow {
Display* m_xDisp;
IWindowCallback* m_callback;
Colormap m_colormapId;
@ -776,6 +656,9 @@ class WindowXlib : public IWindow {
std::unique_ptr<GraphicsContextXlib> m_gfxCtx;
uint32_t m_visualId;
struct timespec m_waitPeriod = {0, static_cast<long int>(1000000000.0/60.0)};
struct timespec m_lastWaitTime = {};
/* Key state trackers (for auto-repeat detection) */
std::unordered_set<unsigned long> m_charKeys;
std::unordered_set<unsigned long> m_specialKeys;
@ -930,6 +813,9 @@ public:
setStyle(EWindowStyle::Default);
setCursor(EMouseCursor::Pointer);
setWindowFrameDefault();
double hz = getWindowRefreshRate();
uint64_t nanos = uint64_t(1000000000.0 / hz);
m_waitPeriod = {nanos / 1000000000, nanos % 1000000000};
XFlush(m_xDisp);
if (!m_gfxCtx->initializeContext(vulkanHandle)) {
@ -1019,6 +905,37 @@ public:
}
}
static double calculateRefreshRate(const XRRModeInfo& mi) {
if (mi.hTotal && mi.vTotal)
return double(mi.dotClock) / (double(mi.hTotal) * double(mi.vTotal));
else
return 60.0;
}
double getWindowRefreshRate() const {
double ret = 60.0;
int nmonitors;
Screen* screen = DefaultScreenOfDisplay(m_xDisp);
XRRMonitorInfo* mInfo = XRRGetMonitors(m_xDisp, screen->root, true, &nmonitors);
if (nmonitors) {
XRRScreenResources* res = XRRGetScreenResourcesCurrent(m_xDisp, screen->root);
XRROutputInfo* oinfo = XRRGetOutputInfo(m_xDisp, res, *mInfo->outputs);
XRRCrtcInfo* ci = XRRGetCrtcInfo(m_xDisp, res, oinfo->crtc);
for (int i = 0; i < res->nmode; ++i) {
const XRRModeInfo& mode = res->modes[i];
if (mode.id == ci->mode) {
ret = calculateRefreshRate(mode);
break;
}
}
XRRFreeCrtcInfo(ci);
XRRFreeOutputInfo(oinfo);
XRRFreeScreenResources(res);
}
XRRFreeMonitors(mInfo);
return ret;
}
void setWindowFrameDefault() {
int x, y, w, h, nmonitors;
Screen* screen = DefaultScreenOfDisplay(m_xDisp);
@ -1279,9 +1196,77 @@ public:
XSendEvent(m_xDisp, se->requestor, False, 0, &reply);
}
void waitForRetrace() {
std::unique_lock<std::mutex> lk(m_gfxCtx->m_vsyncmt);
m_gfxCtx->m_vsynccv.wait(lk);
#define NSEC_PER_SEC 1000000000
static void set_normalized_timespec(struct timespec& ts, time_t sec, int64_t nsec) {
while (nsec >= NSEC_PER_SEC) {
nsec -= NSEC_PER_SEC;
++sec;
}
while (nsec < 0) {
nsec += NSEC_PER_SEC;
--sec;
}
ts.tv_sec = sec;
ts.tv_nsec = nsec;
}
static struct timespec timespec_add(const struct timespec& lhs, const struct timespec& rhs) {
struct timespec ts_delta;
set_normalized_timespec(ts_delta, lhs.tv_sec + rhs.tv_sec,
lhs.tv_nsec + rhs.tv_nsec);
return ts_delta;
}
static struct timespec timespec_sub(const struct timespec& lhs, const struct timespec& rhs) {
struct timespec ts_delta;
set_normalized_timespec(ts_delta, lhs.tv_sec - rhs.tv_sec,
lhs.tv_nsec - rhs.tv_nsec);
return ts_delta;
}
static inline long int timespec_compare(const struct timespec& lhs, const struct timespec& rhs)
{
if (lhs.tv_sec < rhs.tv_sec)
return -1;
if (lhs.tv_sec > rhs.tv_sec)
return 1;
return lhs.tv_nsec - rhs.tv_nsec;
}
int waitForRetrace() {
struct timespec tp;
clock_gettime(CLOCK_REALTIME, &tp);
if (!m_lastWaitTime.tv_sec) {
/* Initialize reference point */
sched_param prio = {75};
sched_setscheduler(0, SCHED_RR, &prio);
m_lastWaitTime = tp;
return 0;
}
m_lastWaitTime = timespec_add(m_lastWaitTime, m_waitPeriod);
long int comp = timespec_compare(m_lastWaitTime, tp);
if (comp == 0) {
/* Exactly at the due date */
return 1;
} else if (comp > 0) {
/* Not at due date yet, sleep here */
struct timespec wait_time = timespec_sub(m_lastWaitTime, tp);
nanosleep(&wait_time, nullptr);
do {
clock_gettime(CLOCK_REALTIME, &tp);
} while (timespec_compare(m_lastWaitTime, tp) > 0);
return 1;
} else {
/* Missed due date, assign next one and return passed cycle count */
int cycles = 0;
do {
m_lastWaitTime = timespec_add(m_lastWaitTime, m_waitPeriod);
++cycles;
} while (timespec_compare(m_lastWaitTime, tp) < 0);
return cycles;
}
}
uintptr_t getPlatformHandle() const { return (uintptr_t)m_windowId; }