/* 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 NULL; 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