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"); 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) void GLXEnableVSync(Display* disp, GLXWindow drawable)
{ {
if (GLXEW_EXT_swap_control) if (GLXEW_EXT_swap_control)

View File

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