Fixed 60Hz timing on GLX

This commit is contained in:
Jack Andersen 2015-11-16 18:20:11 -10:00
parent c5db148e98
commit 49da287791
2 changed files with 70 additions and 33 deletions

View File

@ -13,14 +13,6 @@ void GLXExtensionCheck()
Log.report(LogVisor::FatalError, "swap_control not available");
}
void GLXWaitForVSync()
{
unsigned int sync;
int err = glXWaitVideoSyncSGI(1, 0, &sync);
if (err)
Log.report(LogVisor::FatalError, "wait err");
}
void GLXEnableVSync(Display* disp, GLXWindow drawable)
{
if (GLXEW_EXT_swap_control)

View File

@ -8,6 +8,9 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <GL/glx.h>
@ -41,7 +44,9 @@
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
static glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
typedef int (*glXWaitVideoSyncSGIProc)(int divisor, int remainder, unsigned int* count);
static glXWaitVideoSyncSGIProc glXWaitVideoSyncSGI = 0;
static const int ContextAttribs[] =
{
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
@ -58,7 +63,6 @@ static LogVisor::LogModule Log("boo::WindowXCB");
IGraphicsCommandQueue* _NewGLCommandQueue(IGraphicsContext* parent);
void _XlibUpdateLastGlxCtx(GLXContext lastGlxCtx);
void GLXExtensionCheck();
void GLXWaitForVSync();
void GLXEnableVSync(Display* disp, GLXWindow drawable);
extern int XINPUT_OPCODE;
@ -178,12 +182,14 @@ struct GraphicsContextGLX : IGraphicsContext
int m_visualid = 0;
GLXWindow m_glxWindow = 0;
GLXContext m_glxCtx = 0;
GLXContext m_timerCtx = 0;
IGraphicsCommandQueue* m_commandQueue = nullptr;
IGraphicsDataFactory* m_dataFactory = nullptr;
GLXContext m_loadCtx = 0;
std::thread m_vsyncThread;
bool m_vsyncRunning;
public:
IWindowCallback* m_callback;
@ -265,8 +271,8 @@ public:
glXDestroyWindow(m_xDisp, m_glxWindow);
if (m_loadCtx)
glXDestroyContext(m_xDisp, m_loadCtx);
if (m_timerCtx)
glXDestroyContext(m_xDisp, m_timerCtx);
m_vsyncRunning = false;
m_vsyncThread.join();
}
void _setCallback(IWindowCallback* cb)
@ -291,10 +297,26 @@ public:
m_pf = pf;
}
std::mutex m_vsyncmt;
std::condition_variable m_vsynccv;
void initializeContext()
{
if (!glXCreateContextAttribsARB)
{
glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB");
if (!glXCreateContextAttribsARB)
Log.report(LogVisor::FatalError, "unable to resolve glXCreateContextAttribsARB");
}
if (!glXWaitVideoSyncSGI)
{
glXWaitVideoSyncSGI = (glXWaitVideoSyncSGIProc)
glXGetProcAddressARB((const GLubyte*)"glXWaitVideoSyncSGI");
if (!glXWaitVideoSyncSGI)
Log.report(LogVisor::FatalError, "unable to resolve glXWaitVideoSyncSGI");
}
m_glxCtx = glXCreateContextAttribsARB(m_xDisp, m_fbconfig, m_lastCtx, True, ContextAttribs);
if (!m_glxCtx)
Log.report(LogVisor::FatalError, "unable to make new GLX context");
@ -303,10 +325,45 @@ public:
Log.report(LogVisor::FatalError, "unable to make new GLX window");
_XlibUpdateLastGlxCtx(m_glxCtx);
/* Make additional shared context for vsync timing */
m_timerCtx = glXCreateContextAttribsARB(m_xDisp, m_fbconfig, m_glxCtx, True, ContextAttribs);
if (!m_timerCtx)
Log.report(LogVisor::FatalError, "unable to make new timer GLX context");
/* Spawn vsync thread */
m_vsyncRunning = true;
std::mutex initmt;
std::condition_variable initcv;
std::unique_lock<std::mutex> outerLk(initmt);
m_vsyncThread = std::thread([&]()
{
Display* vsyncDisp;
GLXContext vsyncCtx;
{
std::unique_lock<std::mutex> innerLk(initmt);
vsyncDisp = XOpenDisplay(0);
if (!vsyncDisp)
Log.report(LogVisor::FatalError, "unable to open new vsync display");
vsyncCtx = glXCreateContextAttribsARB(vsyncDisp, m_fbconfig, nullptr, True, ContextAttribs);
if (!vsyncCtx)
Log.report(LogVisor::FatalError, "unable to make new vsync GLX context");
if (!glXMakeCurrent(vsyncDisp, DefaultRootWindow(vsyncDisp), vsyncCtx))
Log.report(LogVisor::FatalError, "unable to make vsync context current");
}
initcv.notify_one();
while (m_vsyncRunning)
{
unsigned int sync;
int err = glXWaitVideoSyncSGI(1, 0, &sync);
if (err)
Log.report(LogVisor::FatalError, "wait err");
m_vsynccv.notify_one();
}
glXMakeCurrent(vsyncDisp, None, nullptr);
glXDestroyContext(vsyncDisp, vsyncCtx);
XCloseDisplay(vsyncDisp);
});
initcv.wait(outerLk);
XUnlockDisplay(m_xDisp);
m_commandQueue = _NewGLCommandQueue(this);
@ -359,18 +416,6 @@ public:
glXSwapBuffers(m_xDisp, m_glxWindow);
}
bool m_timerBound = false;
void bindTimerContext()
{
if (m_timerBound)
return;
XLockDisplay(m_xDisp);
if (!glXMakeContextCurrent(m_xDisp, m_glxWindow, m_glxWindow, m_timerCtx))
Log.report(LogVisor::FatalError, "unable to make timer GLX context current");
XUnlockDisplay(m_xDisp);
m_timerBound = true;
}
};
class WindowXlib : public IWindow
@ -689,8 +734,8 @@ public:
void waitForRetrace()
{
m_gfxCtx.bindTimerContext();
GLXWaitForVSync();
std::unique_lock<std::mutex> lk(m_gfxCtx.m_vsyncmt);
m_gfxCtx.m_vsynccv.wait(lk);
}
uintptr_t getPlatformHandle() const