From d9e43b5d8d7870e81e5fe6e904007b1043a53baa Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Sat, 29 Apr 2017 10:39:53 +0200 Subject: [PATCH] gsdx egl: rewrite the EGL implementation Split code in 2 parts * Base class (GSWndEGL) that implement the core EGL and GL context * Derived class (GSWndEGL_X11/GSWndEGL_WL) that implement the backend to handle native resources Note: Most backend code is only useful for GSopen1/PS1 mode. GSopen2 only requires the AttachNativeWindow implementation Code is based around EGL_EXT_platform extension that allow to select the platform at runtime. Note: I think the extension was integrated in EGL 1.5 The X11 backend was mostly converted to XCB The wayland backend is only a placeholder for future code I don't know if MS windows is/could be supported with EGL_EXT_platform API Code validated on Mesa. Proprietary drivers aren't yet tested. --- plugins/GSdx/GS.cpp | 15 ++- plugins/GSdx/GSWndEGL.cpp | 272 ++++++++++++++++++++++++++------------ plugins/GSdx/GSWndEGL.h | 109 ++++++++++++--- 3 files changed, 295 insertions(+), 101 deletions(-) diff --git a/plugins/GSdx/GS.cpp b/plugins/GSdx/GS.cpp index 93976140e1..2e5e763396 100644 --- a/plugins/GSdx/GS.cpp +++ b/plugins/GSdx/GS.cpp @@ -252,7 +252,20 @@ static int _GSopen(void** dsp, const char* title, GSRendererType renderer, int t #if defined(EGL_SUPPORTED) && defined(__unix__) // Note: EGL code use GLX otherwise maybe it could be also compatible with Windows // Yes OpenGL code isn't complicated enough ! - wnds.push_back(std::make_shared()); + switch (GSWndEGL::SelectPlatform()) { +#if GS_EGL_X11 + case EGL_PLATFORM_X11_KHR: + wnds.push_back(std::make_shared()); + break; +#endif +#if GS_EGL_WL + case EGL_PLATFORM_WAYLAND_KHR: + wnds.push_back(std::make_shared()); + break; +#endif + default: + break; + } #endif #if defined(__unix__) wnds.push_back(std::make_shared()); diff --git a/plugins/GSdx/GSWndEGL.cpp b/plugins/GSdx/GSWndEGL.cpp index 6d72e8b1ee..4716946079 100644 --- a/plugins/GSdx/GSWndEGL.cpp +++ b/plugins/GSdx/GSWndEGL.cpp @@ -24,8 +24,45 @@ #if defined(__unix__) && defined(EGL_SUPPORTED) -GSWndEGL::GSWndEGL() - : m_NativeWindow(0), m_NativeDisplay(NULL) +// static method +int GSWndEGL::SelectPlatform() +{ + // Check the supported extension + const char *client_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + if (!client_extensions) { + fprintf(stderr, "EGL: Client extension not supported\n"); + return 0; + } + fprintf(stdout, "EGL: Supported extensions: %s\n", client_extensions); + + // Check platform extensions are supported (Note: there are core in 1.5) + if (!strstr(client_extensions, "EGL_EXT_platform_base")) { + fprintf(stderr, "EGL: Dynamic platform selection isn't supported\n"); + return 0; + } + + // Finally we can select the platform +#if GS_EGL_X11 + if (strstr(client_extensions, "EGL_EXT_platform_x11")) { + fprintf(stdout, "EGL: select X11 platform\n"); + return EGL_PLATFORM_X11_KHR; + } +#endif +#if GS_EGL_WL + if (strstr(client_extensions, "EGL_EXT_platform_wayland")) { + fprintf(stdout, "EGL: select Wayland platform\n"); + return EGL_PLATFORM_WAYLAND_KHR; + } +#endif + + fprintf(stderr, "EGL: no compatible platform found\n"); + + return 0; +} + + +GSWndEGL::GSWndEGL(int platform) + : m_native_window(nullptr), m_platform(platform) { } @@ -65,7 +102,7 @@ void GSWndEGL::CreateContext(int major, int minor) throw GSDXRecoverableError(); } - m_eglSurface = eglCreateWindowSurface(m_eglDisplay, eglConfig, m_NativeWindow, NULL); + m_eglSurface = eglCreatePlatformWindowSurface(m_eglDisplay, eglConfig, m_native_window, nullptr); if ( m_eglSurface == EGL_NO_SURFACE ) { fprintf(stderr,"EGL: Failed to get a window surface\n"); @@ -128,10 +165,10 @@ void GSWndEGL::BindAPI() bool GSWndEGL::Attach(void* handle, bool managed) { - m_NativeWindow = *(Window*)handle; m_managed = managed; - m_NativeDisplay = XOpenDisplay(NULL); + m_native_window = AttachNativeWindow(handle); + OpenEGLDisplay(); CreateContext(3, 3); @@ -149,22 +186,18 @@ void GSWndEGL::Detach() // The window still need to be closed DetachContext(); eglDestroyContext(m_eglDisplay, m_eglContext); - m_eglContext = NULL; + m_eglContext = nullptr; + eglDestroySurface(m_eglDisplay, m_eglSurface); - m_eglSurface = NULL; + m_eglSurface = nullptr; + CloseEGLDisplay(); - if (m_NativeDisplay) { - XCloseDisplay(m_NativeDisplay); - m_NativeDisplay = NULL; - } + DestroyNativeResources(); } bool GSWndEGL::Create(const string& title, int w, int h) { - if(m_NativeWindow) - throw GSDXRecoverableError(); - if(w <= 0 || h <= 0) { w = theApp.GetConfigI("ModeWidth"); h = theApp.GetConfigI("ModeHeight"); @@ -172,12 +205,9 @@ bool GSWndEGL::Create(const string& title, int w, int h) m_managed = true; - // note this part must be only executed when replaying .gs debug file - m_NativeDisplay = XOpenDisplay(NULL); OpenEGLDisplay(); - m_NativeWindow = XCreateSimpleWindow(m_NativeDisplay, DefaultRootWindow(m_NativeDisplay), 0, 0, w, h, 0, 0, 0); - XMapWindow (m_NativeDisplay, m_NativeWindow); + m_native_window = CreateNativeWindow(w, h); CreateContext(3, 3); @@ -185,16 +215,13 @@ bool GSWndEGL::Create(const string& title, int w, int h) PopulateGlFunction(); - if (m_NativeWindow == 0) - throw GSDXRecoverableError(); - return true; } void* GSWndEGL::GetProcAddress(const char* name, bool opt) { void* ptr = (void*)eglGetProcAddress(name); - if (ptr == NULL) { + if (ptr == nullptr) { fprintf(stderr, "Failed to find %s\n", name); if (!opt) throw GSDXRecoverableError(); @@ -202,49 +229,14 @@ void* GSWndEGL::GetProcAddress(const char* name, bool opt) return ptr; } -void* GSWndEGL::GetDisplay() -{ - // note this part must be only executed when replaying .gs debug file - return (void*)m_NativeDisplay; -} - GSVector4i GSWndEGL::GetClientRect() { - unsigned int h = 480; - unsigned int w = 640; + EGLint h = 0; + EGLint w = 0; + eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_HEIGHT, &h); + eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_WIDTH, &w); - unsigned int borderDummy; - unsigned int depthDummy; - Window winDummy; - int xDummy; - int yDummy; - - if (!m_NativeDisplay) m_NativeDisplay = XOpenDisplay(NULL); - XGetGeometry(m_NativeDisplay, m_NativeWindow, &winDummy, &xDummy, &yDummy, &w, &h, &borderDummy, &depthDummy); - - return GSVector4i(0, 0, (int)w, (int)h); -} - -// Returns FALSE if the window has no title, or if th window title is under the strict -// management of the emulator. - -bool GSWndEGL::SetWindowText(const char* title) -{ - if (!m_managed) return true; - - XTextProperty prop; - - memset(&prop, 0, sizeof(prop)); - - char* ptitle = (char*)title; - if (XStringListToTextProperty(&ptitle, 1, &prop)) { - XSetWMName(m_NativeDisplay, m_NativeWindow, &prop); - } - - XFree(prop.value); - XFlush(m_NativeDisplay); - - return true; + return GSVector4i(0, 0, w, h); } void GSWndEGL::SetVSync(bool enable) @@ -259,23 +251,6 @@ void GSWndEGL::Flip() eglSwapBuffers(m_eglDisplay, m_eglSurface); } -void GSWndEGL::Show() -{ - XMapRaised(m_NativeDisplay, m_NativeWindow); - XFlush(m_NativeDisplay); -} - -void GSWndEGL::Hide() -{ - XUnmapWindow(m_NativeDisplay, m_NativeWindow); - XFlush(m_NativeDisplay); -} - -void GSWndEGL::HideFrame() -{ - // TODO -} - void GSWndEGL::CloseEGLDisplay() { eglReleaseThread(); @@ -284,17 +259,150 @@ void GSWndEGL::CloseEGLDisplay() void GSWndEGL::OpenEGLDisplay() { + // We only need a native display when we manage the window ourself. + // By default, EGL will create its own native display. This way the driver knows + // that display will be thread safe and so it can enable multithread optimization. + void *native_display = (m_managed) ? CreateNativeDisplay() : nullptr; + // Create an EGL display from the native display - m_eglDisplay = eglGetDisplay((EGLNativeDisplayType)m_NativeDisplay); - if ( m_eglDisplay == EGL_NO_DISPLAY ) { + m_eglDisplay = eglGetPlatformDisplay(m_platform, native_display, nullptr); + if (m_eglDisplay == EGL_NO_DISPLAY) { fprintf(stderr,"EGL: Failed to open a display! (0x%x)\n", eglGetError() ); throw GSDXRecoverableError(); } - if ( !eglInitialize(m_eglDisplay, NULL, NULL) ) { + if (!eglInitialize(m_eglDisplay, nullptr, nullptr)) { fprintf(stderr,"EGL: Failed to initialize the display! (0x%x)\n", eglGetError() ); throw GSDXRecoverableError(); } } +////////////////////////////////////////////////////////////////////// +// X11 platform +////////////////////////////////////////////////////////////////////// +#if GS_EGL_X11 + +GSWndEGL_X11::GSWndEGL_X11() + : GSWndEGL(EGL_PLATFORM_X11_KHR), m_NativeDisplay(nullptr), m_NativeWindow(0) +{ +} + +void *GSWndEGL_X11::CreateNativeDisplay() +{ + if (m_NativeDisplay == nullptr) + m_NativeDisplay = XOpenDisplay(nullptr); + + return (void*)m_NativeDisplay; +} + +void *GSWndEGL_X11::CreateNativeWindow(int w, int h) +{ + const int depth = 0, x = 0, y = 0, border_width = 1; +#if 0 + // Old X code in case future code will still require it + m_NativeWindow = XCreateSimpleWindow(m_NativeDisplay, DefaultRootWindow(m_NativeDisplay), 0, 0, w, h, 0, 0, 0); + XMapWindow (m_NativeDisplay, m_NativeWindow); +#endif + + if (m_NativeDisplay == nullptr) { + fprintf(stderr, "EGL X11: display wasn't created before the window\n"); + throw GSDXRecoverableError(); + } + + xcb_connection_t *c = XGetXCBConnection(m_NativeDisplay); + + const xcb_setup_t *setup = xcb_get_setup(c); + + xcb_screen_t *screen = (xcb_setup_roots_iterator (setup)).data; + + m_NativeWindow = xcb_generate_id(c); + + if (m_NativeWindow == 0) { + fprintf(stderr, "EGL X11: failed to create the native window\n"); + throw GSDXRecoverableError(); + } + + xcb_create_window (c, depth, m_NativeWindow, screen->root, x, y, w, h, + border_width, InputOutput, screen->root_visual, 0, nullptr); + + xcb_map_window (c, m_NativeWindow); + + xcb_flush(c); + + return (void*)&m_NativeWindow; +} + +void *GSWndEGL_X11::AttachNativeWindow(void *handle) +{ + m_NativeWindow = *(Window*)handle; + return handle; +} + +void GSWndEGL_X11::DestroyNativeResources() +{ + if (m_NativeDisplay) { + XCloseDisplay(m_NativeDisplay); + m_NativeDisplay = nullptr; + } +} + +bool GSWndEGL_X11::SetWindowText(const char* title) +{ + if (!m_managed) return true; + + xcb_connection_t *c = XGetXCBConnection(m_NativeDisplay); + + xcb_change_property(c, XCB_PROP_MODE_REPLACE, m_NativeWindow, + XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, + strlen (title), title); + + return true; +} + +#endif + +////////////////////////////////////////////////////////////////////// +// Wayland platform (just a place holder) +////////////////////////////////////////////////////////////////////// +#if GS_EGL_WL + +GSWndEGL_WL::GSWndEGL_WL() + : GSWndEGL(EGL_PLATFORM_WAYLAND_KHR), m_NativeDisplay(nullptr), m_NativeWindow(nullptr) +{ +} + +void *GSWndEGL_WL::CreateNativeDisplay() +{ + if (m_NativeDisplay == nullptr) + m_NativeDisplay = wl_display_connect(NULL); + + return (void*)m_NativeDisplay; +} + +void *GSWndEGL_WL::CreateNativeWindow(int w, int h) +{ + return nullptr; +} + +void *GSWndEGL_WL::AttachNativeWindow(void *handle) +{ + m_NativeWindow = (wl_egl_window*)handle; + return handle; +} + +void GSWndEGL_WL::DestroyNativeResources() +{ + if (m_NativeDisplay) { + wl_display_disconnect(m_NativeDisplay); + m_NativeDisplay = nullptr; + } +} + +bool GSWndEGL_WL::SetWindowText(const char* title) +{ + return true; +} + +#endif + #endif diff --git a/plugins/GSdx/GSWndEGL.h b/plugins/GSdx/GSWndEGL.h index 2a3f845564..d72c5f15a0 100644 --- a/plugins/GSdx/GSWndEGL.h +++ b/plugins/GSdx/GSWndEGL.h @@ -22,19 +22,22 @@ #include "GSWnd.h" #if defined(__unix__) && defined(EGL_SUPPORTED) -#include #include #include +#define GS_EGL_X11 1 +#define GS_EGL_WL 0 + class GSWndEGL : public GSWndGL { - EGLNativeWindowType m_NativeWindow; - EGLNativeDisplayType m_NativeDisplay; + void *m_native_window; EGLDisplay m_eglDisplay; EGLSurface m_eglSurface; EGLContext m_eglContext; + int m_platform; + void CreateContext(int major, int minor); void BindAPI(); @@ -42,27 +45,97 @@ class GSWndEGL : public GSWndGL void CloseEGLDisplay(); public: - GSWndEGL(); + GSWndEGL(int platform); virtual ~GSWndEGL() {}; - bool Create(const string& title, int w, int h); - bool Attach(void* handle, bool managed = true); - void Detach(); + bool Create(const string& title, int w, int h) final; + bool Attach(void* handle, bool managed = true) final; + void Detach() final; + + virtual void *CreateNativeDisplay() = 0; + virtual void *CreateNativeWindow(int w, int h) = 0; // GSopen1/PSX API + virtual void *AttachNativeWindow(void *handle) = 0; + virtual void DestroyNativeResources() = 0; - void* GetDisplay(); - void* GetHandle() {return (void*)m_NativeWindow;} GSVector4i GetClientRect(); - bool SetWindowText(const char* title); + virtual bool SetWindowText(const char* title) = 0; // GSopen1/PSX API - void AttachContext(); - void DetachContext(); - void* GetProcAddress(const char* name, bool opt = false); + void AttachContext() final; + void DetachContext() final; + void* GetProcAddress(const char* name, bool opt = false) final; - void Show(); - void Hide(); - void HideFrame(); - void Flip(); - void SetVSync(bool enable); + void Flip() final; + void SetVSync(bool enable) final; + + // Deprecated API + void Show() final {}; + void Hide() final {}; + void HideFrame() final {}; // DX9 API + + virtual void* GetDisplay() = 0; // GSopen1 API + virtual void* GetHandle() = 0; // DX API + + // Static to allow to query supported the platform + // before object creation + static int SelectPlatform(); +}; + +#if GS_EGL_X11 + +#include +#include +#include + +class GSWndEGL_X11 : public GSWndEGL +{ + Display *m_NativeDisplay; + Window m_NativeWindow; + + public: + GSWndEGL_X11(); + virtual ~GSWndEGL_X11() {}; + + void* GetDisplay() final { return (void*)m_NativeDisplay;} + void* GetHandle() final {return (void*)&m_NativeWindow;} + + void *CreateNativeDisplay() final; + void *CreateNativeWindow(int w, int h) final; + void *AttachNativeWindow(void *handle) final; + void DestroyNativeResources() final; + + bool SetWindowText(const char* title) final; }; #endif + +#if GS_EGL_WL + +// Which include ? +#include +#include +#include +#include + +class GSWndEGL_WL : public GSWndEGL +{ + wl_display *m_NativeDisplay; + wl_egl_window *m_NativeWindow; + + public: + GSWndEGL_WL(); + virtual ~GSWndEGL_WL() {}; + + void* GetDisplay() final { return (void*)m_NativeDisplay;} + void* GetHandle() final {return (void*)m_NativeWindow;} + + void *CreateNativeDisplay() final; + void *CreateNativeWindow(int w, int h) final; + void *AttachNativeWindow(void *handle) final; + void DestroyNativeResources() final; + + bool SetWindowText(const char* title) final; +}; + +#endif + +#endif