Files
pcsx2/plugins/GSdx/GSWndEGL.cpp
T
Jonathan Li fecf8e3ed2 gsdx:ogl: Call swap interval function on rendering thread
The swap interval function must be called on the same thread that
rendering takes place on. This fixes an issue where the turbo speed and
frame limiter hotkeys fail to disable vsync when the OpenGL renderer is
used.
2017-08-30 21:08:54 +01:00

408 lines
10 KiB
C++

/*
* Copyright (C) 2007-2012 Gabest
* http://www.gabest.org
*
* 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, 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 GNU Make; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA USA.
* http://www.gnu.org/copyleft/gpl.html
*
*/
#include "stdafx.h"
#include "GSWndEGL.h"
#if defined(__unix__) && defined(EGL_SUPPORTED)
// 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)
{
}
void GSWndEGL::CreateContext(int major, int minor)
{
EGLConfig eglConfig;
EGLint numConfigs = 0;
EGLint contextAttribs[] =
{
EGL_CONTEXT_MAJOR_VERSION_KHR, major,
EGL_CONTEXT_MINOR_VERSION_KHR, minor,
#ifdef ENABLE_OGL_DEBUG
EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR,
#else
// Open Source isn't happy with an unsupported flags...
//EGL_CONTEXT_FLAGS_KHR, GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR,
#endif
EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
EGL_NONE
};
EGLint NullContextAttribs[] = { EGL_NONE };
EGLint attrList[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_DEPTH_SIZE, 0,
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
EGL_NONE
};
BindAPI();
eglChooseConfig(m_eglDisplay, attrList, &eglConfig, 1, &numConfigs);
if ( numConfigs == 0 )
{
fprintf(stderr,"EGL: Failed to get a frame buffer config! (0x%x)\n", eglGetError() );
throw GSDXRecoverableError();
}
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");
throw GSDXRecoverableError();
}
m_eglContext = eglCreateContext(m_eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttribs);
EGLint status = eglGetError();
if (status == EGL_BAD_ATTRIBUTE || status == EGL_BAD_MATCH) {
// Radeon/Gallium don't support advance attribute. Fallback to random value
// Note: Intel gives an EGL_BAD_MATCH. I don't know why but let's by stubborn and retry.
fprintf(stderr, "EGL: warning your driver doesn't support advance openGL context attributes\n");
m_eglContext = eglCreateContext(m_eglDisplay, eglConfig, EGL_NO_CONTEXT, NullContextAttribs);
status = eglGetError();
}
if ( m_eglContext == EGL_NO_CONTEXT )
{
fprintf(stderr,"EGL: Failed to create the context\n");
fprintf(stderr,"EGL STATUS: %x\n", status);
throw GSDXRecoverableError();
}
if ( !eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext) )
{
throw GSDXRecoverableError();
}
}
void GSWndEGL::AttachContext()
{
if (!IsContextAttached()) {
// The setting of the API is local to a thread. This function
// can be called from 2 threads.
BindAPI();
//fprintf(stderr, "Attach the context\n");
eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
m_ctx_attached = true;
}
}
void GSWndEGL::DetachContext()
{
if (IsContextAttached()) {
//fprintf(stderr, "Detach the context\n");
eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
m_ctx_attached = false;
}
}
void GSWndEGL::PopulateWndGlFunction()
{
}
void GSWndEGL::BindAPI()
{
eglBindAPI(EGL_OPENGL_API);
EGLenum api = eglQueryAPI();
if (api != EGL_OPENGL_API) {
fprintf(stderr,"EGL: Failed to bind the OpenGL API got 0x%x instead\n", api);
throw GSDXRecoverableError();
}
}
bool GSWndEGL::Attach(void* handle, bool managed)
{
m_managed = managed;
m_native_window = AttachNativeWindow(handle);
OpenEGLDisplay();
FullContextInit();
return true;
}
void GSWndEGL::Detach()
{
// Actually the destructor is not called when there is only a GSclose/GSshutdown
// The window still need to be closed
DetachContext();
eglDestroyContext(m_eglDisplay, m_eglContext);
m_eglContext = nullptr;
eglDestroySurface(m_eglDisplay, m_eglSurface);
m_eglSurface = nullptr;
CloseEGLDisplay();
DestroyNativeResources();
}
bool GSWndEGL::Create(const string& title, int w, int h)
{
if(w <= 0 || h <= 0) {
w = theApp.GetConfigI("ModeWidth");
h = theApp.GetConfigI("ModeHeight");
}
m_managed = true;
OpenEGLDisplay();
m_native_window = CreateNativeWindow(w, h);
FullContextInit();
return true;
}
void* GSWndEGL::GetProcAddress(const char* name, bool opt)
{
void* ptr = (void*)eglGetProcAddress(name);
if (ptr == nullptr) {
fprintf(stderr, "Failed to find %s\n", name);
if (!opt)
throw GSDXRecoverableError();
}
return ptr;
}
GSVector4i GSWndEGL::GetClientRect()
{
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);
}
void GSWndEGL::SetSwapInterval()
{
// 0 -> disable vsync
// n -> wait n frame
eglSwapInterval(m_eglDisplay, m_vsync);
}
void GSWndEGL::Flip()
{
if (m_vsync_change_requested.exchange(false))
SetSwapInterval();
eglSwapBuffers(m_eglDisplay, m_eglSurface);
}
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();
}
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