Skip to content

Commit

Permalink
gsdx egl: rewrite the EGL implementation
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
gregory38 committed Apr 30, 2017
1 parent 98a5492 commit d9e43b5
Show file tree
Hide file tree
Showing 3 changed files with 288 additions and 94 deletions.
15 changes: 14 additions & 1 deletion plugins/GSdx/GS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<GSWndEGL>());
switch (GSWndEGL::SelectPlatform()) {
#if GS_EGL_X11
case EGL_PLATFORM_X11_KHR:
wnds.push_back(std::make_shared<GSWndEGL_X11>());
break;
#endif
#if GS_EGL_WL
case EGL_PLATFORM_WAYLAND_KHR:
wnds.push_back(std::make_shared<GSWndEGL_WL>());
break;
#endif
default:
break;
}
#endif
#if defined(__unix__)
wnds.push_back(std::make_shared<GSWndOGL>());
Expand Down
258 changes: 183 additions & 75 deletions plugins/GSdx/GSWndEGL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
}

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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);
Expand All @@ -149,152 +186,223 @@ 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");
}

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);

AttachContext();

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();
}
return ptr;
}

void* GSWndEGL::GetDisplay()
GSVector4i GSWndEGL::GetClientRect()
{
// note this part must be only executed when replaying .gs debug file
return (void*)m_NativeDisplay;
EGLint h = 0;
EGLint w = 0;
eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_HEIGHT, &h);
eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_WIDTH, &w);

return GSVector4i(0, 0, w, h);
}

GSVector4i GSWndEGL::GetClientRect()
void GSWndEGL::SetVSync(bool enable)
{
unsigned int h = 480;
unsigned int w = 640;
// 0 -> disable vsync
// n -> wait n frame
eglSwapInterval(m_eglDisplay, enable);
}

unsigned int borderDummy;
unsigned int depthDummy;
Window winDummy;
int xDummy;
int yDummy;
void GSWndEGL::Flip()
{
eglSwapBuffers(m_eglDisplay, m_eglSurface);
}

if (!m_NativeDisplay) m_NativeDisplay = XOpenDisplay(NULL);
XGetGeometry(m_NativeDisplay, m_NativeWindow, &winDummy, &xDummy, &yDummy, &w, &h, &borderDummy, &depthDummy);
void GSWndEGL::CloseEGLDisplay()
{
eglReleaseThread();
eglTerminate(m_eglDisplay);
}

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 = 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();
}

return GSVector4i(0, 0, (int)w, (int)h);
if (!eglInitialize(m_eglDisplay, nullptr, nullptr)) {
fprintf(stderr,"EGL: Failed to initialize the display! (0x%x)\n", eglGetError() );
throw GSDXRecoverableError();
}
}

// Returns FALSE if the window has no title, or if th window title is under the strict
// management of the emulator.
//////////////////////////////////////////////////////////////////////
// X11 platform
//////////////////////////////////////////////////////////////////////
#if GS_EGL_X11

bool GSWndEGL::SetWindowText(const char* title)
GSWndEGL_X11::GSWndEGL_X11()
: GSWndEGL(EGL_PLATFORM_X11_KHR), m_NativeDisplay(nullptr), m_NativeWindow(0)
{
if (!m_managed) return true;
}

XTextProperty prop;
void *GSWndEGL_X11::CreateNativeDisplay()
{
if (m_NativeDisplay == nullptr)
m_NativeDisplay = XOpenDisplay(nullptr);

return (void*)m_NativeDisplay;
}

memset(&prop, 0, sizeof(prop));
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

char* ptitle = (char*)title;
if (XStringListToTextProperty(&ptitle, 1, &prop)) {
XSetWMName(m_NativeDisplay, m_NativeWindow, &prop);
if (m_NativeDisplay == nullptr) {
fprintf(stderr, "EGL X11: display wasn't created before the window\n");
throw GSDXRecoverableError();
}

XFree(prop.value);
XFlush(m_NativeDisplay);
xcb_connection_t *c = XGetXCBConnection(m_NativeDisplay);

return true;
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::SetVSync(bool enable)
void *GSWndEGL_X11::AttachNativeWindow(void *handle)
{
// 0 -> disable vsync
// n -> wait n frame
eglSwapInterval(m_eglDisplay, enable);
m_NativeWindow = *(Window*)handle;
return handle;
}

void GSWndEGL::Flip()
void GSWndEGL_X11::DestroyNativeResources()
{
eglSwapBuffers(m_eglDisplay, m_eglSurface);
if (m_NativeDisplay) {
XCloseDisplay(m_NativeDisplay);
m_NativeDisplay = nullptr;
}
}

void GSWndEGL::Show()
bool GSWndEGL_X11::SetWindowText(const char* title)
{
XMapRaised(m_NativeDisplay, m_NativeWindow);
XFlush(m_NativeDisplay);
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;
}

void GSWndEGL::Hide()
#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)
{
XUnmapWindow(m_NativeDisplay, m_NativeWindow);
XFlush(m_NativeDisplay);
}

void GSWndEGL::HideFrame()
void *GSWndEGL_WL::CreateNativeDisplay()
{
// TODO
if (m_NativeDisplay == nullptr)
m_NativeDisplay = wl_display_connect(NULL);

return (void*)m_NativeDisplay;
}

void GSWndEGL::CloseEGLDisplay()
void *GSWndEGL_WL::CreateNativeWindow(int w, int h)
{
eglReleaseThread();
eglTerminate(m_eglDisplay);
return nullptr;
}

void GSWndEGL::OpenEGLDisplay()
void *GSWndEGL_WL::AttachNativeWindow(void *handle)
{
// Create an EGL display from the native display
m_eglDisplay = eglGetDisplay((EGLNativeDisplayType)m_NativeDisplay);
if ( m_eglDisplay == EGL_NO_DISPLAY ) {
fprintf(stderr,"EGL: Failed to open a display! (0x%x)\n", eglGetError() );
throw GSDXRecoverableError();
}
m_NativeWindow = (wl_egl_window*)handle;
return handle;
}

if ( !eglInitialize(m_eglDisplay, NULL, NULL) ) {
fprintf(stderr,"EGL: Failed to initialize the display! (0x%x)\n", eglGetError() );
throw GSDXRecoverableError();
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
Loading

2 comments on commit d9e43b5

@mirh
Copy link

@mirh mirh commented on d9e43b5 Apr 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EGL_EXT seems just a linux thing.

@gregory38
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well first goal is to have EGL on Linux by default. And drop GLX. Then maybe we could look at EGL on windows. EGL is more important on Linux as it would bring us thread safety. And Wayland support (in long term).

Please sign in to comment.