From c2eb6a2fcb3b9b5dc7c44603914ee49c881a62df Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 13 Dec 2015 18:38:26 +0100 Subject: [PATCH] Minimal port of Android EGL code from Dolphin. --- Common/GL/GLInterface/EGL.cpp | 228 ++++++++++++++++++++++++++ Common/GL/GLInterface/EGL.h | 32 ++++ Common/GL/GLInterface/EGLAndroid.cpp | 28 ++++ Common/GL/GLInterface/EGLAndroid.h | 15 ++ Common/GL/GLInterface/GLInterface.cpp | 40 +++++ Common/GL/GLInterfaceBase.h | 50 ++++++ 6 files changed, 393 insertions(+) create mode 100644 Common/GL/GLInterface/EGL.cpp create mode 100644 Common/GL/GLInterface/EGL.h create mode 100644 Common/GL/GLInterface/EGLAndroid.cpp create mode 100644 Common/GL/GLInterface/EGLAndroid.h create mode 100644 Common/GL/GLInterface/GLInterface.cpp create mode 100644 Common/GL/GLInterfaceBase.h diff --git a/Common/GL/GLInterface/EGL.cpp b/Common/GL/GLInterface/EGL.cpp new file mode 100644 index 0000000000..46d043aa56 --- /dev/null +++ b/Common/GL/GLInterface/EGL.cpp @@ -0,0 +1,228 @@ +// Copyright 2012 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include + +#include "Common/GL/GLInterface/EGL.h" +#include "Common/Log.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; + bool supportsGL = false, supportsGLES2 = false, supportsGLES3 = false; + std::array renderable_types = { + EGL_OPENGL_BIT, + (1 << 6), /* EGL_OPENGL_ES3_BIT_KHR */ + EGL_OPENGL_ES2_BIT, + }; + + for (auto renderable_type : renderable_types) + { + // 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_RENDERABLE_TYPE, renderable_type, + EGL_NONE + }; + + // Get how many configs there are + if (!eglChooseConfig( egl_dpy, attribs, nullptr, 0, &num_configs)) + { + INFO_LOG(G3D, "Error: couldn't get an EGL visual config\n"); + continue; + } + + EGLConfig* config = new EGLConfig[num_configs]; + + // Get all the configurations + if (!eglChooseConfig(egl_dpy, attribs, config, num_configs, &num_configs)) + { + INFO_LOG(G3D, "Error: couldn't get an EGL visual config\n"); + delete[] config; + continue; + } + + 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; + } + } + delete[] config; + } + + 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; + + if (s_opengl_mode == GLInterfaceMode::MODE_DETECT) // Errored before we found a mode + s_opengl_mode = GLInterfaceMode::MODE_OPENGL; // Fall back to OpenGL +} + +// Create rendering window. +// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() +bool cInterfaceEGL::Create(void *window_handle, bool core) +{ + const char *s; + EGLint egl_major, egl_minor; + + egl_dpy = OpenDisplay(); + + if (!egl_dpy) + { + INFO_LOG(G3D, "Error: eglGetDisplay() failed\n"); + return false; + } + + if (!eglInitialize(egl_dpy, &egl_major, &egl_minor)) + { + INFO_LOG(G3D, "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(G3D, "Unknown opengl mode set\n"); + return false; + break; + } + + if (!eglChooseConfig( egl_dpy, attribs, &config, 1, &num_configs)) + { + INFO_LOG(G3D, "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(G3D, "EGL_VERSION = %s\n", s); + + s = eglQueryString(egl_dpy, EGL_VENDOR); + INFO_LOG(G3D, "EGL_VENDOR = %s\n", s); + + s = eglQueryString(egl_dpy, EGL_EXTENSIONS); + INFO_LOG(G3D, "EGL_EXTENSIONS = %s\n", s); + + s = eglQueryString(egl_dpy, EGL_CLIENT_APIS); + INFO_LOG(G3D, "EGL_CLIENT_APIS = %s\n", s); + + egl_ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs ); + if (!egl_ctx) + { + INFO_LOG(G3D, "Error: eglCreateContext failed\n"); + exit(1); + } + + egl_surf = eglCreateWindowSurface(egl_dpy, config, native_window, nullptr); + if (!egl_surf) + { + INFO_LOG(G3D, "Error: eglCreateWindowSurface failed\n"); + exit(1); + } + + return true; +} + +bool cInterfaceEGL::MakeCurrent() +{ + return eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx); +} + +bool cInterfaceEGL::ClearCurrent() +{ + return eglMakeCurrent(egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); +} + +// Close backend +void cInterfaceEGL::Shutdown() +{ + ShutdownPlatform(); + if (egl_ctx && !eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx)) + NOTICE_LOG(G3D, "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(G3D, "Could not destroy drawing context."); + if (!eglDestroySurface(egl_dpy, egl_surf)) + NOTICE_LOG(G3D, "Could not destroy window surface."); + if (!eglTerminate(egl_dpy)) + NOTICE_LOG(G3D, "Could not destroy display connection."); + egl_ctx = nullptr; + egl_dpy = nullptr; + } +} diff --git a/Common/GL/GLInterface/EGL.h b/Common/GL/GLInterface/EGL.h new file mode 100644 index 0000000000..f25b129d90 --- /dev/null +++ b/Common/GL/GLInterface/EGL.h @@ -0,0 +1,32 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/GL/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 core); + bool MakeCurrent(); + bool ClearCurrent(); + void Shutdown(); +}; diff --git a/Common/GL/GLInterface/EGLAndroid.cpp b/Common/GL/GLInterface/EGLAndroid.cpp new file mode 100644 index 0000000000..6cf8a956bf --- /dev/null +++ b/Common/GL/GLInterface/EGLAndroid.cpp @@ -0,0 +1,28 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/GL/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); + + const int width = ANativeWindow_getWidth(host_window); + const int height = ANativeWindow_getHeight(host_window); + SetBackBufferDimensions(width, height); + + return host_window; +} + +void cInterfaceEGLAndroid::ShutdownPlatform() +{ +} + diff --git a/Common/GL/GLInterface/EGLAndroid.h b/Common/GL/GLInterface/EGLAndroid.h new file mode 100644 index 0000000000..7f3ede889c --- /dev/null +++ b/Common/GL/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 "Common/GL/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/Common/GL/GLInterface/GLInterface.cpp b/Common/GL/GLInterface/GLInterface.cpp new file mode 100644 index 0000000000..8abe67dc71 --- /dev/null +++ b/Common/GL/GLInterface/GLInterface.cpp @@ -0,0 +1,40 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/GL/GLInterfaceBase.h" + +#ifdef ANDROID +#include "Common/GL/GLInterface/EGLAndroid.h" +#elif defined(__APPLE__) +#include "Common/GL/GLInterface/AGL.h" +#elif defined(_WIN32) +#include "Common/GL/GLInterface/WGL.h" +#elif HAVE_X11 +#if defined(USE_EGL) && USE_EGL +#include "Common/GL/GLInterface/EGLX11.h" +#else +#include "Common/GL/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/Common/GL/GLInterfaceBase.h b/Common/GL/GLInterfaceBase.h new file mode 100644 index 0000000000..85bd412499 --- /dev/null +++ b/Common/GL/GLInterfaceBase.h @@ -0,0 +1,50 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "Common/CommonTypes.h" + +enum GLInterfaceMode { + MODE_DETECT = 0, + MODE_OPENGL, + MODE_OPENGLES2, + MODE_OPENGLES3, +}; + +class cInterfaceBase +{ +protected: + // Window dimensions. + u32 s_backbuffer_width; + u32 s_backbuffer_height; + + u32 s_opengl_mode; +public: + cInterfaceBase() : s_backbuffer_width(0), s_backbuffer_height(0), s_opengl_mode(MODE_DETECT) {} + virtual ~cInterfaceBase() {} + virtual void Swap() {} + virtual void SetMode(u32 mode) { s_opengl_mode = GLInterfaceMode::MODE_OPENGL; } + virtual u32 GetMode() { return s_opengl_mode; } + virtual void* GetFuncAddress(const std::string& name) { return nullptr; } + virtual bool Create(void *window_handle, bool core = true) { return true; } + virtual bool MakeCurrent() { return true; } + virtual bool ClearCurrent() { return true; } + virtual void Shutdown() {} + + virtual void SwapInterval(int Interval) { } + virtual u32 GetBackBufferWidth() { return s_backbuffer_width; } + virtual u32 GetBackBufferHeight() { return s_backbuffer_height; } + virtual void SetBackBufferDimensions(u32 W, u32 H) {s_backbuffer_width = W; s_backbuffer_height = H; } + virtual void Update() { } + virtual bool PeekMessages() { return false; } +}; + +extern cInterfaceBase *GLInterface; + +// This function has to be defined along the Host_ functions from Core/Host.h. +// Current canonical implementation: DolphinWX/GLInterface/GLInterface.cpp. +cInterfaceBase* HostGL_CreateGLInterface();