diff options
| author | Augustin Cavalier <waddlesplash@gmail.com> | 2014-11-01 10:33:02 -0400 |
|---|---|---|
| committer | Augustin Cavalier <waddlesplash@gmail.com> | 2014-11-02 12:16:33 -0500 |
| commit | 29593d403b7e0b2311241de868426ed285c7d97a (patch) | |
| tree | 858bd8dbf27a6f1a0f88f79ee1b2430f8952e6e2 /Source/Core/VideoBackends/OGL/GLInterface | |
| parent | 089e32ba7d7f48993fa0706f7b9f69a12bf7192a (diff) | |
Move GLInterface to the OGL VideoBackend's directory.
Diffstat (limited to 'Source/Core/VideoBackends/OGL/GLInterface')
17 files changed, 1358 insertions, 0 deletions
diff --git a/Source/Core/VideoBackends/OGL/GLInterface/AGL.cpp b/Source/Core/VideoBackends/OGL/GLInterface/AGL.cpp new file mode 100644 index 0000000000..4ab1cdd565 --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/AGL.cpp @@ -0,0 +1,109 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "VideoBackends/OGL/GLInterface/AGL.h" + +#include "VideoCommon/RenderBase.h" +#include "VideoCommon/VertexShaderManager.h" +#include "VideoCommon/VideoConfig.h" + +void cInterfaceAGL::Swap() +{ + [cocoaCtx flushBuffer]; +} + +// Create rendering window. +// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() +bool cInterfaceAGL::Create(void *window_handle) +{ + cocoaWin = reinterpret_cast<NSView*>(window_handle); + NSSize size = [cocoaWin frame].size; + + // Enable high-resolution display support. + [cocoaWin setWantsBestResolutionOpenGLSurface:YES]; + + NSWindow *window = [cocoaWin window]; + + float scale = [window backingScaleFactor]; + size.width *= scale; + size.height *= scale; + + // Control window size and picture scaling + s_backbuffer_width = size.width; + s_backbuffer_height = size.height; + + NSOpenGLPixelFormatAttribute attr[] = { NSOpenGLPFADoubleBuffer, NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, NSOpenGLPFAAccelerated, 0 }; + NSOpenGLPixelFormat *fmt = [[NSOpenGLPixelFormat alloc] + initWithAttributes: attr]; + if (fmt == nil) + { + ERROR_LOG(VIDEO, "failed to create pixel format"); + return false; + } + + cocoaCtx = [[NSOpenGLContext alloc] initWithFormat: fmt shareContext: nil]; + [fmt release]; + if (cocoaCtx == nil) + { + ERROR_LOG(VIDEO, "failed to create context"); + return false; + } + + if (cocoaWin == nil) + { + ERROR_LOG(VIDEO, "failed to create window"); + return false; + } + + [window makeFirstResponder:cocoaWin]; + [cocoaCtx setView: cocoaWin]; + [window makeKeyAndOrderFront: nil]; + + return true; +} + +bool cInterfaceAGL::MakeCurrent() +{ + [cocoaCtx makeCurrentContext]; + return true; +} + +bool cInterfaceAGL::ClearCurrent() +{ + [NSOpenGLContext clearCurrentContext]; + return true; +} + +// Close backend +void cInterfaceAGL::Shutdown() +{ + [cocoaCtx clearDrawable]; + [cocoaCtx release]; + cocoaCtx = nil; +} + +void cInterfaceAGL::Update() +{ + NSWindow *window = [cocoaWin window]; + NSSize size = [cocoaWin frame].size; + + float scale = [window backingScaleFactor]; + size.width *= scale; + size.height *= scale; + + if (s_backbuffer_width == size.width && + s_backbuffer_height == size.height) + return; + + s_backbuffer_width = size.width; + s_backbuffer_height = size.height; + + [cocoaCtx update]; +} + +void cInterfaceAGL::SwapInterval(int interval) +{ + [cocoaCtx setValues:(GLint *)&interval forParameter:NSOpenGLCPSwapInterval]; +} + diff --git a/Source/Core/VideoBackends/OGL/GLInterface/AGL.h b/Source/Core/VideoBackends/OGL/GLInterface/AGL.h new file mode 100644 index 0000000000..8ab0dedc0c --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/AGL.h @@ -0,0 +1,27 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#ifdef __APPLE__ +#import <AppKit/AppKit.h> +#endif + +#include "VideoBackends/OGL/GLInterfaceBase.h" + +class cInterfaceAGL : public cInterfaceBase +{ +private: + NSView *cocoaWin; + NSOpenGLContext *cocoaCtx; +public: + void Swap(); + bool Create(void *window_handle); + bool MakeCurrent(); + bool ClearCurrent(); + void Shutdown(); + void Update(); + void SwapInterval(int interval); + +}; diff --git a/Source/Core/VideoBackends/OGL/GLInterface/EGL.cpp b/Source/Core/VideoBackends/OGL/GLInterface/EGL.cpp new file mode 100644 index 0000000000..0fdec471ef --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/EGL.cpp @@ -0,0 +1,209 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "VideoBackends/OGL/GLInterfaceBase.h" +#include "VideoBackends/OGL/GLInterface/EGL.h" +#include "VideoCommon/RenderBase.h" + +// Show the current FPS +void cInterfaceEGL::Swap() +{ + eglSwapBuffers(egl_dpy, egl_surf); +} +void cInterfaceEGL::SwapInterval(int Interval) +{ + eglSwapInterval(egl_dpy, Interval); +} + +void* cInterfaceEGL::GetFuncAddress(const std::string& name) +{ + return (void*)eglGetProcAddress(name.c_str()); +} + +void cInterfaceEGL::DetectMode() +{ + if (s_opengl_mode != MODE_DETECT) + return; + + EGLint num_configs; + EGLConfig *config = nullptr; + bool supportsGL = false, supportsGLES2 = false, supportsGLES3 = false; + + // attributes for a visual in RGBA format with at least + // 8 bits per color + int attribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_NONE }; + + // Get how many configs there are + if (!eglChooseConfig( egl_dpy, attribs, nullptr, 0, &num_configs)) + { + INFO_LOG(VIDEO, "Error: couldn't get an EGL visual config\n"); + goto err_exit; + } + + config = new EGLConfig[num_configs]; + + // Get all the configurations + if (!eglChooseConfig(egl_dpy, attribs, config, num_configs, &num_configs)) + { + INFO_LOG(VIDEO, "Error: couldn't get an EGL visual config\n"); + goto err_exit; + } + + for (int i = 0; i < num_configs; ++i) + { + EGLint attribVal; + bool ret; + ret = eglGetConfigAttrib(egl_dpy, config[i], EGL_RENDERABLE_TYPE, &attribVal); + if (ret) + { + if (attribVal & EGL_OPENGL_BIT) + supportsGL = true; + if (attribVal & (1 << 6)) /* EGL_OPENGL_ES3_BIT_KHR */ + supportsGLES3 = true; + if (attribVal & EGL_OPENGL_ES2_BIT) + supportsGLES2 = true; + } + } + if (supportsGL) + s_opengl_mode = GLInterfaceMode::MODE_OPENGL; + else if (supportsGLES3) + s_opengl_mode = GLInterfaceMode::MODE_OPENGLES3; + else if (supportsGLES2) + s_opengl_mode = GLInterfaceMode::MODE_OPENGLES2; +err_exit: + if (s_opengl_mode == GLInterfaceMode::MODE_DETECT) // Errored before we found a mode + s_opengl_mode = GLInterfaceMode::MODE_OPENGL; // Fall back to OpenGL + if (config) + delete[] config; +} + +// Create rendering window. +// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() +bool cInterfaceEGL::Create(void *window_handle) +{ + const char *s; + EGLint egl_major, egl_minor; + + egl_dpy = OpenDisplay(); + + if (!egl_dpy) + { + INFO_LOG(VIDEO, "Error: eglGetDisplay() failed\n"); + return false; + } + + if (!eglInitialize(egl_dpy, &egl_major, &egl_minor)) + { + INFO_LOG(VIDEO, "Error: eglInitialize() failed\n"); + return false; + } + + /* Detection code */ + EGLConfig config; + EGLint num_configs; + + DetectMode(); + + // attributes for a visual in RGBA format with at least + // 8 bits per color + int attribs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_NONE }; + + EGLint ctx_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + switch (s_opengl_mode) + { + case MODE_OPENGL: + attribs[1] = EGL_OPENGL_BIT; + ctx_attribs[0] = EGL_NONE; + break; + case MODE_OPENGLES2: + attribs[1] = EGL_OPENGL_ES2_BIT; + ctx_attribs[1] = 2; + break; + case MODE_OPENGLES3: + attribs[1] = (1 << 6); /* EGL_OPENGL_ES3_BIT_KHR */ + ctx_attribs[1] = 3; + break; + default: + ERROR_LOG(VIDEO, "Unknown opengl mode set\n"); + return false; + break; + } + + if (!eglChooseConfig( egl_dpy, attribs, &config, 1, &num_configs)) + { + INFO_LOG(VIDEO, "Error: couldn't get an EGL visual config\n"); + exit(1); + } + + if (s_opengl_mode == MODE_OPENGL) + eglBindAPI(EGL_OPENGL_API); + else + eglBindAPI(EGL_OPENGL_ES_API); + + EGLNativeWindowType host_window = (EGLNativeWindowType) window_handle; + EGLNativeWindowType native_window = InitializePlatform(host_window, config); + + s = eglQueryString(egl_dpy, EGL_VERSION); + INFO_LOG(VIDEO, "EGL_VERSION = %s\n", s); + + s = eglQueryString(egl_dpy, EGL_VENDOR); + INFO_LOG(VIDEO, "EGL_VENDOR = %s\n", s); + + s = eglQueryString(egl_dpy, EGL_EXTENSIONS); + INFO_LOG(VIDEO, "EGL_EXTENSIONS = %s\n", s); + + s = eglQueryString(egl_dpy, EGL_CLIENT_APIS); + INFO_LOG(VIDEO, "EGL_CLIENT_APIS = %s\n", s); + + egl_ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs ); + if (!egl_ctx) + { + INFO_LOG(VIDEO, "Error: eglCreateContext failed\n"); + exit(1); + } + + egl_surf = eglCreateWindowSurface(egl_dpy, config, native_window, nullptr); + if (!egl_surf) + { + INFO_LOG(VIDEO, "Error: eglCreateWindowSurface failed\n"); + exit(1); + } + + return true; +} + +bool cInterfaceEGL::MakeCurrent() +{ + return eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx); +} +// Close backend +void cInterfaceEGL::Shutdown() +{ + ShutdownPlatform(); + if (egl_ctx && !eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx)) + NOTICE_LOG(VIDEO, "Could not release drawing context."); + if (egl_ctx) + { + eglMakeCurrent(egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (!eglDestroyContext(egl_dpy, egl_ctx)) + NOTICE_LOG(VIDEO, "Could not destroy drawing context."); + if (!eglDestroySurface(egl_dpy, egl_surf)) + NOTICE_LOG(VIDEO, "Could not destroy window surface."); + if (!eglTerminate(egl_dpy)) + NOTICE_LOG(VIDEO, "Could not destroy display connection."); + egl_ctx = nullptr; + } +} diff --git a/Source/Core/VideoBackends/OGL/GLInterface/EGL.h b/Source/Core/VideoBackends/OGL/GLInterface/EGL.h new file mode 100644 index 0000000000..1ed8ab59a0 --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/EGL.h @@ -0,0 +1,32 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <EGL/egl.h> + +#include "Core/ConfigManager.h" +#include "VideoBackends/OGL/GLInterfaceBase.h" + +class cInterfaceEGL : public cInterfaceBase +{ +protected: + void DetectMode(); + EGLSurface egl_surf; + EGLContext egl_ctx; + EGLDisplay egl_dpy; + + virtual EGLDisplay OpenDisplay() = 0; + virtual EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) = 0; + virtual void ShutdownPlatform() = 0; +public: + void SwapInterval(int Interval); + void Swap(); + void SetMode(u32 mode) { s_opengl_mode = mode; } + void* GetFuncAddress(const std::string& name); + bool Create(void *window_handle); + bool MakeCurrent(); + void Shutdown(); +}; diff --git a/Source/Core/VideoBackends/OGL/GLInterface/EGLAndroid.cpp b/Source/Core/VideoBackends/OGL/GLInterface/EGLAndroid.cpp new file mode 100644 index 0000000000..f6c567ee6c --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/EGLAndroid.cpp @@ -0,0 +1,29 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "Core/Host.h" +#include "VideoBackends/OGL/GLInterface/EGLAndroid.h" + +EGLDisplay cInterfaceEGLAndroid::OpenDisplay() +{ + return eglGetDisplay(EGL_DEFAULT_DISPLAY); +} + +EGLNativeWindowType cInterfaceEGLAndroid::InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) +{ + EGLint format; + eglGetConfigAttrib(egl_dpy, config, EGL_NATIVE_VISUAL_ID, &format); + ANativeWindow_setBuffersGeometry(host_window, 0, 0, format); + + int none, width, height; + Host_GetRenderWindowSize(none, none, width, height); + GLInterface->SetBackBufferDimensions(width, height); + + return host_window; +} + +void cInterfaceEGLAndroid::ShutdownPlatform() +{ +} + diff --git a/Source/Core/VideoBackends/OGL/GLInterface/EGLAndroid.h b/Source/Core/VideoBackends/OGL/GLInterface/EGLAndroid.h new file mode 100644 index 0000000000..fe0b873759 --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/EGLAndroid.h @@ -0,0 +1,15 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "VideoBackends/OGL/GLInterface/EGL.h" + +class cInterfaceEGLAndroid : public cInterfaceEGL +{ +protected: + EGLDisplay OpenDisplay() override; + EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) override; + void ShutdownPlatform() override; +}; diff --git a/Source/Core/VideoBackends/OGL/GLInterface/EGLX11.cpp b/Source/Core/VideoBackends/OGL/GLInterface/EGLX11.cpp new file mode 100644 index 0000000000..9b89d2fba2 --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/EGLX11.cpp @@ -0,0 +1,44 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "VideoBackends/OGL/GLInterface/EGLX11.h" + +EGLDisplay cInterfaceEGLX11::OpenDisplay() +{ + dpy = XOpenDisplay(nullptr); + XWindow.Initialize(dpy); + return eglGetDisplay(dpy); +} + +EGLNativeWindowType cInterfaceEGLX11::InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) +{ + EGLint vid; + eglGetConfigAttrib(egl_dpy, config, EGL_NATIVE_VISUAL_ID, &vid); + + XVisualInfo visTemplate; + visTemplate.visualid = vid; + + XVisualInfo *vi; + int nVisuals; + vi = XGetVisualInfo(dpy, VisualIDMask, &visTemplate, &nVisuals); + + XWindowAttributes attribs; + if (!XGetWindowAttributes(dpy, (Window)host_window, &attribs)) + { + ERROR_LOG(VIDEO, "Window attribute retrieval failed"); + return 0; + } + + s_backbuffer_width = attribs.width; + s_backbuffer_height = attribs.height; + + return (EGLNativeWindowType) XWindow.CreateXWindow((Window) host_window, vi); +} + +void cInterfaceEGLX11::ShutdownPlatform() +{ + XWindow.DestroyXWindow(); + XCloseDisplay(dpy); +} + diff --git a/Source/Core/VideoBackends/OGL/GLInterface/EGLX11.h b/Source/Core/VideoBackends/OGL/GLInterface/EGLX11.h new file mode 100644 index 0000000000..f858046e0d --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/EGLX11.h @@ -0,0 +1,21 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include <X11/Xlib.h> + +#include "VideoBackends/OGL/GLInterface/EGL.h" +#include "VideoBackends/OGL/GLInterface/X11_Util.h" + +class cInterfaceEGLX11 : public cInterfaceEGL +{ +private: + cX11Window XWindow; + Display *dpy; +protected: + EGLDisplay OpenDisplay() override; + EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) override; + void ShutdownPlatform() override; +}; diff --git a/Source/Core/VideoBackends/OGL/GLInterface/GLInterface.cpp b/Source/Core/VideoBackends/OGL/GLInterface/GLInterface.cpp new file mode 100644 index 0000000000..29af99fa41 --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/GLInterface.cpp @@ -0,0 +1,40 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "VideoBackends/OGL/GLInterfaceBase.h" + +#ifdef ANDROID +#include "VideoBackends/OGL/GLInterface/EGLAndroid.h" +#elif defined(__APPLE__) +#include "VideoBackends/OGL/GLInterface/AGL.h" +#elif defined(_WIN32) +#include "VideoBackends/OGL/GLInterface/WGL.h" +#elif HAVE_X11 +#if defined(USE_EGL) && USE_EGL +#include "VideoBackends/OGL/GLInterface/EGLX11.h" +#else +#include "VideoBackends/OGL/GLInterface/GLX.h" +#endif +#else +#error Platform doesnt have a GLInterface +#endif + +cInterfaceBase* HostGL_CreateGLInterface() +{ + #ifdef ANDROID + return new cInterfaceEGLAndroid; + #elif defined(__APPLE__) + return new cInterfaceAGL; + #elif defined(_WIN32) + return new cInterfaceWGL; + #elif defined(HAVE_X11) && HAVE_X11 + #if defined(USE_EGL) && USE_EGL + return new cInterfaceEGLX11; + #else + return new cInterfaceGLX; + #endif + #else + return nullptr; + #endif +} diff --git a/Source/Core/VideoBackends/OGL/GLInterface/GLX.cpp b/Source/Core/VideoBackends/OGL/GLInterface/GLX.cpp new file mode 100644 index 0000000000..12912b0f5d --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/GLX.cpp @@ -0,0 +1,145 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include <string> + +#include "VideoBackends/OGL/GLInterface/GLX.h" + +#include "VideoCommon/RenderBase.h" +#include "VideoCommon/VideoConfig.h" + +typedef int ( * PFNGLXSWAPINTERVALSGIPROC) (int interval); +static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = nullptr; + +void cInterfaceGLX::SwapInterval(int Interval) +{ + if (glXSwapIntervalSGI) + glXSwapIntervalSGI(Interval); + else + ERROR_LOG(VIDEO, "No support for SwapInterval (framerate clamped to monitor refresh rate)."); +} +void* cInterfaceGLX::GetFuncAddress(const std::string& name) +{ + return (void*)glXGetProcAddress((const GLubyte*)name.c_str()); +} + +void cInterfaceGLX::Swap() +{ + glXSwapBuffers(dpy, win); +} + +// Create rendering window. +// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() +bool cInterfaceGLX::Create(void *window_handle) +{ + int glxMajorVersion, glxMinorVersion; + + // attributes for a single buffered visual in RGBA format with at least + // 8 bits per color + int attrListSgl[] = {GLX_RGBA, GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + None}; + + // attributes for a double buffered visual in RGBA format with at least + // 8 bits per color + int attrListDbl[] = {GLX_RGBA, GLX_DOUBLEBUFFER, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + None }; + + int attrListDefault[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + None }; + + dpy = XOpenDisplay(nullptr); + int screen = DefaultScreen(dpy); + + glXQueryVersion(dpy, &glxMajorVersion, &glxMinorVersion); + NOTICE_LOG(VIDEO, "glX-Version %d.%d", glxMajorVersion, glxMinorVersion); + + // Get an appropriate visual + vi = glXChooseVisual(dpy, screen, attrListDbl); + if (vi == nullptr) + { + vi = glXChooseVisual(dpy, screen, attrListSgl); + if (vi != nullptr) + { + ERROR_LOG(VIDEO, "Only single buffered visual!"); + } + else + { + vi = glXChooseVisual(dpy, screen, attrListDefault); + if (vi == nullptr) + { + ERROR_LOG(VIDEO, "Could not choose visual (glXChooseVisual)"); + return false; + } + } + } + else + { + NOTICE_LOG(VIDEO, "Got double buffered visual!"); + } + + // Create a GLX context. + ctx = glXCreateContext(dpy, vi, nullptr, GL_TRUE); + if (!ctx) + { + PanicAlert("Unable to create GLX context."); + return false; + } + + XWindow.Initialize(dpy); + + Window parent = (Window)window_handle; + + XWindowAttributes attribs; + if (!XGetWindowAttributes(dpy, parent, &attribs)) + { + ERROR_LOG(VIDEO, "Window attribute retrieval failed"); + return false; + } + + s_backbuffer_width = attribs.width; + s_backbuffer_height = attribs.height; + + win = XWindow.CreateXWindow(parent, vi); + return true; +} + +bool cInterfaceGLX::MakeCurrent() +{ + bool success = glXMakeCurrent(dpy, win, ctx); + if (success) + { + // load this function based on the current bound context + glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)GLInterface->GetFuncAddress("glXSwapIntervalSGI"); + } + return success; +} + +bool cInterfaceGLX::ClearCurrent() +{ + return glXMakeCurrent(dpy, None, nullptr); +} + + +// Close backend +void cInterfaceGLX::Shutdown() +{ + XWindow.DestroyXWindow(); + if (ctx) + { + glXDestroyContext(dpy, ctx); + XCloseDisplay(dpy); + ctx = nullptr; + } +} + diff --git a/Source/Core/VideoBackends/OGL/GLInterface/GLX.h b/Source/Core/VideoBackends/OGL/GLInterface/GLX.h new file mode 100644 index 0000000000..fa97efdabf --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/GLX.h @@ -0,0 +1,30 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <GL/glx.h> + +#include "VideoBackends/OGL/GLInterfaceBase.h" +#include "VideoBackends/OGL/GLInterface/X11_Util.h" + +class cInterfaceGLX : public cInterfaceBase +{ +private: + cX11Window XWindow; + Display *dpy; + Window win; + GLXContext ctx; + XVisualInfo *vi; +public: + friend class cX11Window; + void SwapInterval(int Interval) override; + void Swap() override; + void* GetFuncAddress(const std::string& name) override; + bool Create(void *window_handle); + bool MakeCurrent() override; + bool ClearCurrent() override; + void Shutdown() override; +}; diff --git a/Source/Core/VideoBackends/OGL/GLInterface/WGL.cpp b/Source/Core/VideoBackends/OGL/GLInterface/WGL.cpp new file mode 100644 index 0000000000..232eb7ec71 --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/WGL.cpp @@ -0,0 +1,182 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include <string> + +#include "Core/Host.h" + +#include "VideoBackends/OGL/GLInterface/WGL.h" + +#include "VideoCommon/RenderBase.h" +#include "VideoCommon/VertexShaderManager.h" +#include "VideoCommon/VideoConfig.h" + +static HDC hDC = nullptr; // Private GDI Device Context +static HGLRC hRC = nullptr; // Permanent Rendering Context +static HINSTANCE dllHandle = nullptr; // Handle to OpenGL32.dll + +// typedef from wglext.h +typedef BOOL(WINAPI * PFNWGLSWAPINTERVALEXTPROC) (int interval); +static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr; + +void cInterfaceWGL::SwapInterval(int Interval) +{ + if (wglSwapIntervalEXT) + wglSwapIntervalEXT(Interval); + else + ERROR_LOG(VIDEO, "No support for SwapInterval (framerate clamped to monitor refresh rate)."); +} +void cInterfaceWGL::Swap() +{ + SwapBuffers(hDC); +} + +void* cInterfaceWGL::GetFuncAddress(const std::string& name) +{ + void* func = (void*)wglGetProcAddress((LPCSTR)name.c_str()); + if (func == nullptr) + func = (void*)GetProcAddress(dllHandle, (LPCSTR)name.c_str()); + return func; +} + +// Draw messages on top of the screen +bool cInterfaceWGL::PeekMessages() +{ + // TODO: peekmessage + MSG msg; + while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) + { + if (msg.message == WM_QUIT) + return FALSE; + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return TRUE; +} + +// Create rendering window. +// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() +bool cInterfaceWGL::Create(void *window_handle) +{ + if (window_handle == nullptr) + return false; + + HWND window_handle_reified = reinterpret_cast<HWND>(window_handle); + RECT window_rect = {0}; + + if (!GetClientRect(window_handle_reified, &window_rect)) + return false; + + // Control window size and picture scaling + int twidth = (window_rect.right - window_rect.left); + int theight = (window_rect.bottom - window_rect.top); + s_backbuffer_width = twidth; + s_backbuffer_height = theight; + + m_window_handle = window_handle_reified; + +#ifdef _WIN32 + dllHandle = LoadLibrary(TEXT("OpenGL32.dll")); +#endif + + PIXELFORMATDESCRIPTOR pfd = // pfd Tells Windows How We Want Things To Be + { + sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor + 1, // Version Number + PFD_DRAW_TO_WINDOW | // Format Must Support Window + PFD_SUPPORT_OPENGL | // Format Must Support OpenGL + PFD_DOUBLEBUFFER, // Must Support Double Buffering + PFD_TYPE_RGBA, // Request An RGBA Format + 32, // Select Our Color Depth + 0, 0, 0, 0, 0, 0, // Color Bits Ignored + 0, // 8bit Alpha Buffer + 0, // Shift Bit Ignored + 0, // No Accumulation Buffer + 0, 0, 0, 0, // Accumulation Bits Ignored + 0, // 0Bit Z-Buffer (Depth Buffer) + 0, // 0bit Stencil Buffer + 0, // No Auxiliary Buffer + PFD_MAIN_PLANE, // Main Drawing Layer + 0, // Reserved + 0, 0, 0 // Layer Masks Ignored + }; + + int PixelFormat; // Holds The Results After Searching For A Match + + if (!(hDC = GetDC(window_handle_reified))) + { + PanicAlert("(1) Can't create an OpenGL Device context. Fail."); + return false; + } + + if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd))) + { + PanicAlert("(2) Can't find a suitable PixelFormat."); + return false; + } + + if (!SetPixelFormat(hDC, PixelFormat, &pfd)) + { + PanicAlert("(3) Can't set the PixelFormat."); + return false; + } + + if (!(hRC = wglCreateContext(hDC))) + { + PanicAlert("(4) Can't create an OpenGL rendering context."); + return false; + } + + return true; +} + +bool cInterfaceWGL::MakeCurrent() +{ + bool success = wglMakeCurrent(hDC, hRC) ? true : false; + if (success) + { + // Grab the swap interval function pointer + wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)GLInterface->GetFuncAddress("wglSwapIntervalEXT"); + } + return success; +} + +bool cInterfaceWGL::ClearCurrent() +{ + return wglMakeCurrent(hDC, nullptr) ? true : false; +} + +// Update window width, size and etc. Called from Render.cpp +void cInterfaceWGL::Update() +{ + RECT rcWindow; + GetClientRect(m_window_handle, &rcWindow); + + // Get the new window width and height + s_backbuffer_width = (rcWindow.right - rcWindow.left); + s_backbuffer_height = (rcWindow.bottom - rcWindow.top); +} + +// Close backend +void cInterfaceWGL::Shutdown() +{ + if (hRC) + { + if (!wglMakeCurrent(nullptr, nullptr)) + NOTICE_LOG(VIDEO, "Could not release drawing context."); + + if (!wglDeleteContext(hRC)) + ERROR_LOG(VIDEO, "Attempt to release rendering context failed."); + + hRC = nullptr; + } + + if (hDC && !ReleaseDC(m_window_handle, hDC)) + { + ERROR_LOG(VIDEO, "Attempt to release device context failed."); + hDC = nullptr; + } +} + + diff --git a/Source/Core/VideoBackends/OGL/GLInterface/WGL.h b/Source/Core/VideoBackends/OGL/GLInterface/WGL.h new file mode 100644 index 0000000000..ea0c22c9d1 --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/WGL.h @@ -0,0 +1,25 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include "VideoBackends/OGL/GLInterfaceBase.h" + +class cInterfaceWGL : public cInterfaceBase +{ +public: + void SwapInterval(int Interval); + void Swap(); + void* GetFuncAddress(const std::string& name); + bool Create(void *window_handle); + bool MakeCurrent(); + bool ClearCurrent(); + void Shutdown(); + + void Update(); + bool PeekMessages(); + + HWND m_window_handle; +}; diff --git a/Source/Core/VideoBackends/OGL/GLInterface/X11Utils.cpp b/Source/Core/VideoBackends/OGL/GLInterface/X11Utils.cpp new file mode 100644 index 0000000000..7832269e27 --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/X11Utils.cpp @@ -0,0 +1,278 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include <spawn.h> +#include <unistd.h> +#include <sys/wait.h> + +#include "Common/Logging/Log.h" +#include "Core/ConfigManager.h" +#include "Core/Core.h" +#include "Core/CoreParameter.h" +#include "VideoBackends/OGL/GLInterface/X11Utils.h" + +extern char **environ; + +#if defined(HAVE_WX) && HAVE_WX +#include <algorithm> +#include <string> + +#include "DolphinWX/WxUtils.h" +#endif + +namespace X11Utils +{ + +void ToggleFullscreen(Display *dpy, Window win) +{ + // Init X event structure for _NET_WM_STATE_FULLSCREEN client message + XEvent event; + event.xclient.type = ClientMessage; + event.xclient.message_type = XInternAtom(dpy, "_NET_WM_STATE", False); + event.xclient.window = win; + event.xclient.format = 32; + event.xclient.data.l[0] = _NET_WM_STATE_TOGGLE; + event.xclient.data.l[1] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + + // Send the event + if (!XSendEvent(dpy, DefaultRootWindow(dpy), False, + SubstructureRedirectMask | SubstructureNotifyMask, &event)) + ERROR_LOG(VIDEO, "Failed to switch fullscreen/windowed mode."); +} + +void InhibitScreensaver(Display *dpy, Window win, bool suspend) +{ + char id[11]; + snprintf(id, sizeof(id), "0x%lx", win); + + // Call xdg-screensaver + char *argv[4] = { + (char *)"xdg-screensaver", + (char *)(suspend ? "suspend" : "resume"), + id, + nullptr}; + pid_t pid; + if (!posix_spawnp(&pid, "xdg-screensaver", nullptr, nullptr, argv, environ)) + { + int status; + while (waitpid (pid, &status, 0) == -1); + + DEBUG_LOG(VIDEO, "Started xdg-screensaver (PID = %d)", (int)pid); + } +} + +#if defined(HAVE_XRANDR) && HAVE_XRANDR +XRRConfiguration::XRRConfiguration(Display *_dpy, Window _win) + : dpy(_dpy) + , win(_win) + , screenResources(nullptr), outputInfo(nullptr), crtcInfo(nullptr) + , fullMode(0) + , fs_fb_width(0), fs_fb_height(0), fs_fb_width_mm(0), fs_fb_height_mm(0) + , bValid(true), bIsFullscreen(false) +{ + int XRRMajorVersion, XRRMinorVersion; + + if (!XRRQueryVersion(dpy, &XRRMajorVersion, &XRRMinorVersion) || + (XRRMajorVersion < 1 || (XRRMajorVersion == 1 && XRRMinorVersion < 3))) + { + WARN_LOG(VIDEO, "XRRExtension not supported."); + bValid = false; + return; + } + + screenResources = XRRGetScreenResourcesCurrent(dpy, win); + + screen = DefaultScreen(dpy); + fb_width = DisplayWidth(dpy, screen); + fb_height = DisplayHeight(dpy, screen); + fb_width_mm = DisplayWidthMM(dpy, screen); + fb_height_mm = DisplayHeightMM(dpy, screen); + + INFO_LOG(VIDEO, "XRRExtension-Version %d.%d", XRRMajorVersion, XRRMinorVersion); + Update(); +} + +XRRConfiguration::~XRRConfiguration() +{ + if (bValid && bIsFullscreen) + ToggleDisplayMode(False); + if (screenResources) + XRRFreeScreenResources(screenResources); + if (outputInfo) + XRRFreeOutputInfo(outputInfo); + if (crtcInfo) + XRRFreeCrtcInfo(crtcInfo); +} + +void XRRConfiguration::Update() +{ + if (SConfig::GetInstance().m_LocalCoreStartupParameter.strFullscreenResolution == "Auto") + return; + + if (!bValid) + return; + + if (outputInfo) + { + XRRFreeOutputInfo(outputInfo); + outputInfo = nullptr; + } + if (crtcInfo) + { + XRRFreeCrtcInfo(crtcInfo); + crtcInfo = nullptr; + } + fullMode = 0; + + // Get the resolution setings for fullscreen mode + unsigned int fullWidth, fullHeight; + char *output_name = nullptr; + if (SConfig::GetInstance().m_LocalCoreStartupParameter.strFullscreenResolution.find(':') == + std::string::npos) + { + fullWidth = fb_width; + fullHeight = fb_height; + } + else + { + sscanf(SConfig::GetInstance().m_LocalCoreStartupParameter.strFullscreenResolution.c_str(), + "%m[^:]: %ux%u", &output_name, &fullWidth, &fullHeight); + } + + for (int i = 0; i < screenResources->noutput; i++) + { + XRROutputInfo *output_info = XRRGetOutputInfo(dpy, screenResources, screenResources->outputs[i]); + if (output_info && output_info->crtc && output_info->connection == RR_Connected) + { + XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(dpy, screenResources, output_info->crtc); + if (crtc_info) + { + if (!output_name || !strcmp(output_name, output_info->name)) + { + // Use the first output for the default setting. + if (!output_name) + { + output_name = strdup(output_info->name); + SConfig::GetInstance().m_LocalCoreStartupParameter.strFullscreenResolution = + StringFromFormat("%s: %ux%u", output_info->name, fullWidth, fullHeight); + } + outputInfo = output_info; + crtcInfo = crtc_info; + for (int j = 0; j < output_info->nmode && fullMode == 0; j++) + { + for (int k = 0; k < screenResources->nmode && fullMode == 0; k++) + { + if (output_info->modes[j] == screenResources->modes[k].id) + { + if (fullWidth == screenResources->modes[k].width && + fullHeight == screenResources->modes[k].height) + { + fullMode = screenResources->modes[k].id; + if (crtcInfo->x + (int)screenResources->modes[k].width > fs_fb_width) + fs_fb_width = crtcInfo->x + screenResources->modes[k].width; + if (crtcInfo->y + (int)screenResources->modes[k].height > fs_fb_height) + fs_fb_height = crtcInfo->y + screenResources->modes[k].height; + } + } + } + } + } + else + { + if (crtc_info->x + (int)crtc_info->width > fs_fb_width) + fs_fb_width = crtc_info->x + crtc_info->width; + if (crtc_info->y + (int)crtc_info->height > fs_fb_height) + fs_fb_height = crtc_info->y + crtc_info->height; + } + } + if (crtc_info && crtcInfo != crtc_info) + XRRFreeCrtcInfo(crtc_info); + } + if (output_info && outputInfo != output_info) + XRRFreeOutputInfo(output_info); + } + fs_fb_width_mm = fs_fb_width * DisplayHeightMM(dpy, screen) / DisplayHeight(dpy, screen); + fs_fb_height_mm = fs_fb_height * DisplayHeightMM(dpy, screen) / DisplayHeight(dpy, screen); + + if (output_name) + free(output_name); + + if (outputInfo && crtcInfo && fullMode) + { + INFO_LOG(VIDEO, "Fullscreen Resolution %dx%d", fullWidth, fullHeight); + } + else + { + ERROR_LOG(VIDEO, "Failed to obtain fullscreen size.\n" + "Using current desktop resolution for fullscreen."); + } +} + +void XRRConfiguration::ToggleDisplayMode(bool bFullscreen) +{ + if (!bValid || !screenResources || !outputInfo || !crtcInfo || !fullMode) + return; + if (bFullscreen == bIsFullscreen) + return; + + XGrabServer(dpy); + if (bFullscreen) + { + XRRSetCrtcConfig(dpy, screenResources, outputInfo->crtc, CurrentTime, + crtcInfo->x, crtcInfo->y, fullMode, crtcInfo->rotation, + crtcInfo->outputs, crtcInfo->noutput); + XRRSetScreenSize(dpy, win, fs_fb_width, fs_fb_height, fs_fb_width_mm, fs_fb_height_mm); + bIsFullscreen = true; + } + else + { + XRRSetCrtcConfig(dpy, screenResources, outputInfo->crtc, CurrentTime, + crtcInfo->x, crtcInfo->y, crtcInfo->mode, crtcInfo->rotation, + crtcInfo->outputs, crtcInfo->noutput); + XRRSetScreenSize(dpy, win, fb_width, fb_height, fb_width_mm, fb_height_mm); + bIsFullscreen = false; + } + XUngrabServer(dpy); + XSync(dpy, false); +} + +void XRRConfiguration::AddResolutions(std::vector<std::string>& resos) +{ + if (!bValid || !screenResources) + return; + + //Get all full screen resolutions for the config dialog + for (int i = 0; i < screenResources->noutput; i++) + { + XRROutputInfo *output_info = + XRRGetOutputInfo(dpy, screenResources, screenResources->outputs[i]); + + if (output_info && output_info->crtc && output_info->connection == RR_Connected) + { + for (int j = 0; j < output_info->nmode; j++) + { + for (int k = 0; k < screenResources->nmode; k++) + { + if (output_info->modes[j] == screenResources->modes[k].id) + { + const std::string strRes = + std::string(output_info->name) + ": " + + std::string(screenResources->modes[k].name); + // Only add unique resolutions + if (std::find(resos.begin(), resos.end(), strRes) == resos.end()) + { + resos.push_back(strRes); + } + } + } + } + } + if (output_info) + XRRFreeOutputInfo(output_info); + } +} + +#endif + +} diff --git a/Source/Core/VideoBackends/OGL/GLInterface/X11Utils.h b/Source/Core/VideoBackends/OGL/GLInterface/X11Utils.h new file mode 100644 index 0000000000..b68a1babe6 --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/X11Utils.h @@ -0,0 +1,71 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +// HACK: Xlib.h (included from gtk/gdk headers and directly) uses #defines on +// common names such as "Status", "BadRequest" or "Response", causing SFML +// headers to be completely broken. +// +// We work around that issue by including SFML first before X11 headers. This +// is terrible, but such is the life with Xlib. +#include <SFML/Network.hpp> // NOLINT + +#if defined(HAVE_WX) && HAVE_WX +#include <gtk/gtk.h> +#include <gdk/gdkx.h> +#include <wx/arrstr.h> +#endif + +#if defined(HAVE_XRANDR) && HAVE_XRANDR +#include <X11/extensions/Xrandr.h> +#endif +#include <X11/X.h> +#include <X11/Xlib.h> + + +// EWMH state actions, see +// http://freedesktop.org/wiki/Specifications/wm-spec?action=show&redirect=Standards%2Fwm-spec +#define _NET_WM_STATE_REMOVE 0 // remove/unset property +#define _NET_WM_STATE_ADD 1 // add/set property +#define _NET_WM_STATE_TOGGLE 2 // toggle property + +namespace X11Utils +{ + +void ToggleFullscreen(Display *dpy, Window win); +#if defined(HAVE_WX) && HAVE_WX +Window XWindowFromHandle(void *Handle); +Display *XDisplayFromHandle(void *Handle); +#endif + +void InhibitScreensaver(Display *dpy, Window win, bool suspend); + +#if defined(HAVE_XRANDR) && HAVE_XRANDR +class XRRConfiguration +{ + public: + XRRConfiguration(Display *_dpy, Window _win); + ~XRRConfiguration(); + + void Update(); + void ToggleDisplayMode(bool bFullscreen); + void AddResolutions(std::vector<std::string>& resos); + + private: + Display *dpy; + Window win; + int screen; + XRRScreenResources *screenResources; + XRROutputInfo *outputInfo; + XRRCrtcInfo *crtcInfo; + RRMode fullMode; + int fb_width, fb_height, fb_width_mm, fb_height_mm; + int fs_fb_width, fs_fb_height, fs_fb_width_mm, fs_fb_height_mm; + bool bValid; + bool bIsFullscreen; +}; +#endif + +} diff --git a/Source/Core/VideoBackends/OGL/GLInterface/X11_Util.cpp b/Source/Core/VideoBackends/OGL/GLInterface/X11_Util.cpp new file mode 100644 index 0000000000..ffc48550bb --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/X11_Util.cpp @@ -0,0 +1,75 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "Common/Thread.h" +#include "Core/Host.h" +#include "VideoBackends/OGL/GLInterfaceBase.h" +#include "VideoBackends/OGL/GLInterface/X11_Util.h" +#include "VideoCommon/VideoConfig.h" + +void cX11Window::Initialize(Display *_dpy) +{ + dpy = _dpy; +} + +Window cX11Window::CreateXWindow(Window parent, XVisualInfo *vi) +{ + XSetWindowAttributes attr; + + colormap = XCreateColormap(dpy, parent, vi->visual, AllocNone); + + // Setup window attributes + attr.colormap = colormap; + + XWindowAttributes attribs; + if (!XGetWindowAttributes(dpy, parent, &attribs)) + { + ERROR_LOG(VIDEO, "Window attribute retrieval failed"); + return 0; + } + + // Create the window + win = XCreateWindow(dpy, parent, + 0, 0, attribs.width, attribs.height, 0, + vi->depth, InputOutput, vi->visual, + CWColormap, &attr); + XSelectInput(dpy, parent, StructureNotifyMask); + XMapWindow(dpy, win); + XSync(dpy, True); + + xEventThread = std::thread(&cX11Window::XEventThread, this); + + return win; +} + +void cX11Window::DestroyXWindow(void) +{ + XUnmapWindow(dpy, win); + win = 0; + if (xEventThread.joinable()) + xEventThread.join(); + XFreeColormap(dpy, colormap); +} + +void cX11Window::XEventThread() +{ + while (win) + { + XEvent event; + for (int num_events = XPending(dpy); num_events > 0; num_events--) + { + XNextEvent(dpy, &event); + switch (event.type) + { + case ConfigureNotify: + XResizeWindow(dpy, win, event.xconfigure.width, event.xconfigure.height); + GLInterface->SetBackBufferDimensions(event.xconfigure.width, event.xconfigure.height); + break; + default: + break; + } + } + Common::SleepCurrentThread(20); + } +} diff --git a/Source/Core/VideoBackends/OGL/GLInterface/X11_Util.h b/Source/Core/VideoBackends/OGL/GLInterface/X11_Util.h new file mode 100644 index 0000000000..1facad52aa --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLInterface/X11_Util.h @@ -0,0 +1,26 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <thread> +#include <X11/keysym.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +class cX11Window +{ +private: + void XEventThread(); + std::thread xEventThread; + Colormap colormap; +public: + void Initialize(Display *dpy); + Window CreateXWindow(Window parent, XVisualInfo *vi); + void DestroyXWindow(); + + Display *dpy; + Window win; +}; |
