pcsx2/plugins/zzogl-pg/opengl/GLWinX11.cpp

509 lines
13 KiB
C++

/* ZZ Open GL graphics plugin
* Copyright (c)2009-2010 zeydlitz@gmail.com, arcum42@gmail.com
* Based on Zerofrog's ZeroGS KOSMOS (c)2005-2008
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "Util.h"
#include "GLWin.h"
#if defined(GL_X11_WINDOW)
#ifdef EGL_API
// Need at least MESA 9.0 (plan for october/november 2012)
// So force the destiny to at least check the compilation
#ifndef EGL_KHR_create_context
#define EGL_KHR_create_context 1
#define EGL_CONTEXT_MAJOR_VERSION_KHR EGL_CONTEXT_CLIENT_VERSION
#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30FB
#define EGL_CONTEXT_FLAGS_KHR 0x30FC
#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30FD
#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31BD
#define EGL_NO_RESET_NOTIFICATION_KHR 0x31BE
#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31BF
#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001
#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002
#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004
#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001
#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002
#endif
#endif
#ifdef USE_GSOPEN2
bool GLWindow::CreateWindow(void *pDisplay)
{
bool ret = true;
NativeWindow = (Window)*((u32*)(pDisplay)+1);
// Do not take the display which come from pcsx2 neither change it.
// You need a new one to do the operation in the GS thread
NativeDisplay = XOpenDisplay(NULL);
if (!NativeDisplay) ret = false;
#ifdef EGL_API
if (!OpenEGLDisplay()) ret = false;
#endif
return ret;
}
#else
bool GLWindow::CreateWindow(void *pDisplay)
{
bool ret = true;
// init support of multi thread
if (!XInitThreads())
ZZLog::Error_Log("Failed to init the xlib concurent threads");
NativeDisplay = XOpenDisplay(NULL);
if (!NativeDisplay) ret = false;
if (pDisplay == NULL)
{
ZZLog::Error_Log("Failed to create window. Exiting...");
return false;
}
// Allow pad to use the display
*(Display**)pDisplay = NativeDisplay;
// Pad can use the window to grab the input. For the moment just set to 0 to avoid
// to grab an unknow window... Anyway GSopen1 might be dropped in the future
*((u32*)(pDisplay)+1) = 0;
#ifdef EGL_API
if (!OpenEGLDisplay()) ret = false;
#endif
return ret;
}
#endif
#ifdef EGL_API
EGLBoolean GLWindow::OpenEGLDisplay()
{
// Create an EGL display from the native display
eglDisplay = eglGetDisplay((EGLNativeDisplayType)NativeDisplay);
if ( eglDisplay == EGL_NO_DISPLAY ) return EGL_FALSE;
if ( !eglInitialize(eglDisplay, NULL, NULL) ) return EGL_FALSE;
return EGL_TRUE;
}
#endif
#ifdef EGL_API
void GLWindow::CloseEGLDisplay()
{
eglTerminate(eglDisplay);
}
#endif
bool GLWindow::ReleaseContext()
{
bool status = true;
#ifdef GLX_API
if (!NativeDisplay) return status;
// free the context
if (glxContext)
{
if (!glXMakeCurrent(NativeDisplay, None, NULL)) {
ZZLog::Error_Log("GLX: Could not release drawing context.");
status = false;
}
glXDestroyContext(NativeDisplay, glxContext);
glxContext = NULL;
}
#endif
#ifdef EGL_API
eglReleaseThread();
#endif
return status;
}
void GLWindow::CloseWindow()
{
SaveConfig();
if (!NativeDisplay) return;
#ifdef EGL_API
CloseEGLDisplay();
#endif
XCloseDisplay(NativeDisplay);
NativeDisplay = NULL;
}
void GLWindow::GetWindowSize()
{
if (!NativeDisplay or !NativeWindow) return;
u32 depth = 0;
#ifdef GLX_API
unsigned int borderDummy;
Window winDummy;
s32 xDummy;
s32 yDummy;
u32 width;
u32 height;
XLockDisplay(NativeDisplay);
XGetGeometry(NativeDisplay, NativeWindow, &winDummy, &xDummy, &yDummy, &width, &height, &borderDummy, &depth);
XUnlockDisplay(NativeDisplay);
#endif
// FIXME: Not sure it works but that could remove latest X11 bits.
#ifdef EGL_API
int width;
int height;
eglQuerySurface(eglDisplay, eglSurface, EGL_WIDTH, &width);
eglQuerySurface(eglDisplay, eglSurface, EGL_HEIGHT, &height);
#endif
// update the gl buffer size
UpdateWindowSize(width, height);
#ifndef USE_GSOPEN2
// too verbose!
ZZLog::Dev_Log("Resolution %dx%d. Depth %d bpp. Position (%d,%d)", width, height, depth, conf.x, conf.y);
#endif
}
void GLWindow::PrintProtocolVersion()
{
#ifdef GLX_API
int glxMajorVersion, glxMinorVersion;
glXQueryVersion(NativeDisplay, &glxMajorVersion, &glxMinorVersion);
if (glXIsDirect(NativeDisplay, glxContext))
ZZLog::Error_Log("glX-Version %d.%d with Direct Rendering", glxMajorVersion, glxMinorVersion);
else
ZZLog::Error_Log("glX-Version %d.%d with Indirect Rendering !!! It will be slow", glxMajorVersion, glxMinorVersion);
#endif
#ifdef EGL_API
ZZLog::Error_Log("EGL: %s : %s", eglQueryString(eglDisplay, EGL_VENDOR) , eglQueryString(eglDisplay, EGL_VERSION) );
ZZLog::Error_Log("EGL: extensions supported: %s", eglQueryString(eglDisplay, EGL_EXTENSIONS));
#endif
}
#ifdef GLX_API
bool GLWindow::CreateContextGL(int major, int minor)
{
if (!NativeDisplay) return false;
// Get visual information
int attrListDbl[] =
{
// GLX_X_RENDERABLE: If True is specified, then only frame buffer configurations that have associated X
// visuals (and can be used to render to Windows and/or GLX pixmaps) will be considered. The default value is GLX_DONT_CARE.
GLX_X_RENDERABLE , True,
GLX_RED_SIZE , 8,
GLX_GREEN_SIZE , 8,
GLX_BLUE_SIZE , 8,
GLX_DEPTH_SIZE , 24,
GLX_DOUBLEBUFFER , True,
None
};
// Attribute are very sensible to the various implementation (intel, nvidia, amd)
// Nvidia and Intel doesn't support previous attributes for opengl2.0
int attrListDbl_2_0[] =
{
GLX_RGBA,
GLX_DOUBLEBUFFER,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_DEPTH_SIZE, 24,
None
};
// Only keep for older card but NVIDIA and AMD both drop the support of those cards
if (major <= 2) {
XVisualInfo *vi = glXChooseVisual(NativeDisplay, DefaultScreen(NativeDisplay), attrListDbl_2_0);
if (vi == NULL) return false;
glxContext = glXCreateContext(NativeDisplay, vi, NULL, GL_TRUE);
XFree(vi);
if (!glxContext) return false;
glXMakeCurrent(NativeDisplay, NativeWindow, glxContext);
return true;
}
PFNGLXCHOOSEFBCONFIGPROC glXChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC)GetProcAddress("glXChooseFBConfig");
int fbcount = 0;
GLXFBConfig *fbc = glXChooseFBConfig(NativeDisplay, DefaultScreen(NativeDisplay), attrListDbl, &fbcount);
if (!fbc || fbcount < 1) {
ZZLog::Error_Log("GLX: failed to find a framebuffer");
return false;
}
PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)GetProcAddress("glXCreateContextAttribsARB");
if (!glXCreateContextAttribsARB) return false;
// Create a context
int context_attribs[] =
{
GLX_CONTEXT_MAJOR_VERSION_ARB, major,
GLX_CONTEXT_MINOR_VERSION_ARB, minor,
// Keep compatibility for old cruft
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
//GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB | GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
// FIXME : Request a debug context to ease opengl development
#if (defined(ZEROGS_DEVBUILD) || defined(_DEBUG)) && defined(OGL4_LOG)
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB,
#endif
None
};
glxContext = glXCreateContextAttribsARB(NativeDisplay, fbc[0], 0, true, context_attribs);
if (!glxContext) {
ZZLog::Error_Log("GLX: failed to create an opengl context");
return false;
}
XSync( NativeDisplay, false);
if (!glXMakeCurrent(NativeDisplay, NativeWindow, glxContext)) {
ZZLog::Error_Log("GLX: failed to attach the opengl context");
return false;
}
return true;
}
#endif
bool GLWindow::CreateContextGL()
{
bool ret;
#if defined(OGL4_LOG) || defined(GLSL4_API)
// We need to define a debug context. So we need at a 3.0 context (if not 3.2 actually)
ret = CreateContextGL(3, 3);
#else
// FIXME there was some issue with previous context creation on Geforce7. Code was rewritten
// for GSdx unfortunately it was not tested on Geforce7 so keep the 2.0 context for now.
// Note: Geforce 6&7 was dropped from nvidia driver (2012)
#if 0
ret = CreateContextGL(3, 0)
if (! ret )
ret = CreateContextGL(2, 0);
#else
ret = CreateContextGL(2, 0);
#endif
#endif
return ret;
}
#ifdef EGL_API
bool GLWindow::CreateContextGL(int major, int minor)
{
EGLConfig eglConfig;
EGLint numConfigs;
EGLint contextAttribs[] =
{
EGL_CONTEXT_MAJOR_VERSION_KHR, major,
EGL_CONTEXT_MINOR_VERSION_KHR, minor,
// Keep compatibility for old cruft
EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR,
//EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR | EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR,
// FIXME : Request a debug context to ease opengl development
#if defined(ZEROGS_DEVBUILD) || defined(_DEBUG)
EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR,
#endif
EGL_NONE
};
EGLint attrList[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_DEPTH_SIZE, 24,
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
EGL_NONE
};
eglBindAPI(EGL_OPENGL_API);
if ( !eglChooseConfig(eglDisplay, attrList, &eglConfig, 1, &numConfigs) )
{
ZZLog::Error_Log("EGL: Failed to get a frame buffer config!");
return EGL_FALSE;
}
eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, NativeWindow, NULL);
if ( eglSurface == EGL_NO_SURFACE )
{
ZZLog::Error_Log("EGL: Failed to get a window surface");
return EGL_FALSE;
}
eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttribs );
if ( eglContext == EGL_NO_CONTEXT )
{
ZZLog::Error_Log("EGL: Failed to create the context");
ZZLog::Error_Log("EGL STATUS: %x", eglGetError());
return EGL_FALSE;
}
if ( !eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext) )
{
return EGL_FALSE;
}
return EGL_TRUE;
}
#endif
#ifdef USE_GSOPEN2
bool GLWindow::DisplayWindow(int _width, int _height)
{
GetWindowSize();
if ( !CreateContextGL() ) return false;
PrintProtocolVersion();
return true;
}
#else
bool GLWindow::DisplayWindow(int _width, int _height)
{
backbuffer.w = _width;
backbuffer.h = _height;
NativeWindow = XCreateSimpleWindow(NativeDisplay, DefaultRootWindow(NativeDisplay), conf.x, conf.y, backbuffer.w, backbuffer.h, 0, 0, 0);
// Draw the window
XMapRaised(NativeDisplay, NativeWindow);
XSync(NativeDisplay, false);
if ( !CreateContextGL() ) return false;
PrintProtocolVersion();
GetWindowSize();
return true;
}
#endif
void* GLWindow::GetProcAddress(const char *function)
{
#ifdef EGL_API
return (void*)eglGetProcAddress(function);
#endif
#ifdef GLX_API
return (void*)glXGetProcAddress((const GLubyte*)function);
#endif
}
void GLWindow::SwapGLBuffers()
{
if (glGetError() != GL_NO_ERROR) ZZLog::Debug_Log("glError before swap!");
ZZLog::Check_GL_Error();
#ifdef GLX_API
glXSwapBuffers(NativeDisplay, NativeWindow);
#endif
#ifdef EGL_API
eglSwapBuffers(eglDisplay, eglSurface);
#endif
// glClear(GL_COLOR_BUFFER_BIT);
}
void GLWindow::InitVsync(bool extension)
{
#ifdef GLX_API
vsync_supported = false;
if (extension) {
swapinterval = (_PFNSWAPINTERVAL)GetProcAddress("glXSwapInterval");
if (!swapinterval)
swapinterval = (_PFNSWAPINTERVAL)GetProcAddress("glXSwapIntervalSGI");
if (!swapinterval)
swapinterval = (_PFNSWAPINTERVAL)GetProcAddress("glXSwapIntervalEXT");
if (swapinterval) {
swapinterval(0);
vsync_supported = true;
} else {
ZZLog::Error_Log("GLX: No support for SwapInterval (framerate clamped to monitor refresh rate),");
}
}
#endif
}
void GLWindow::SetVsync(bool enable)
{
fprintf(stderr, "change vsync %d\n", enable);
#ifdef EGL_API
eglSwapInterval(eglDisplay, enable);
#endif
#ifdef GLX_API
if (vsync_supported && swapinterval) {
swapinterval(enable);
}
#endif
}
u32 THR_KeyEvent = 0; // Value for key event processing between threads
bool THR_bShift = false;
bool THR_bCtrl = false;
void GLWindow::ProcessEvents()
{
FUNCLOG
#ifdef USE_GSOPEN2
GetWindowSize();
#endif
if (THR_KeyEvent) // This value was passed from GSKeyEvents which could be in another thread
{
int my_KeyEvent = THR_KeyEvent;
bool my_bShift = THR_bShift;
bool my_bCtrl = THR_bCtrl;
THR_KeyEvent = 0;
switch (my_KeyEvent)
{
case XK_F5:
case XK_F6:
case XK_F7:
case XK_F9:
// Note: to avoid some clash with PCSX2 shortcut in GSOpen2.
// GS shortcut will only be activated when ctrl is press
if (my_bCtrl)
OnFKey(my_KeyEvent - XK_F1 + 1, my_bShift);
break;
}
}
}
// ************************** Function that are useless in GSopen2 (GSopen 1 is only used with the debug replayer)
void GLWindow::SetTitle(char *strtitle) { }
#endif