/* Created on: Oct 18, 2019 Copyright 2019 flyinghead This file is part of Flycast. Flycast 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 of the License, or (at your option) any later version. Flycast 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 Flycast. If not, see . */ #include "gl_context.h" #ifdef USE_EGL #include "types.h" #include "cfg/option.h" #ifdef __ANDROID__ #include // requires ndk r5 or newer #endif EGLGraphicsContext theGLContext; bool EGLGraphicsContext::makeCurrent() { if (surface == EGL_NO_SURFACE || context == EGL_NO_CONTEXT) return false; return eglMakeCurrent(display, surface, surface, context); } bool EGLGraphicsContext::init() { int version = gladLoaderLoadEGL(EGL_NO_DISPLAY); if (version == 0) { ERROR_LOG(RENDERER, "Failed to load libEGL.so"); return false; } NOTICE_LOG(RENDERER, "EGL version %d.%d", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)); //try to get a display display = eglGetDisplay((EGLNativeDisplayType)display); //if failed, get the default display (this will not happen in win32) if (display == EGL_NO_DISPLAY) display = eglGetDisplay(EGL_DEFAULT_DISPLAY); // Initialise EGL EGLint maj, min; if (!eglInitialize(display, &maj, &min)) { ERROR_LOG(RENDERER, "EGL Error: eglInitialize failed"); return false; } gladLoaderLoadEGL(display); if (surface == EGL_NO_SURFACE) { EGLint pi32ConfigAttribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_NONE }; int num_config; EGLConfig config; if (!eglChooseConfig(display, pi32ConfigAttribs, &config, 1, &num_config) || (num_config != 1)) { ERROR_LOG(RENDERER, "EGL Error: eglChooseConfig failed"); return false; } #ifdef __ANDROID__ EGLint format; if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) { ERROR_LOG(RENDERER, "eglGetConfigAttrib() returned error %x", eglGetError()); return false; } ANativeWindow_setBuffersGeometry((ANativeWindow *)window, 0, 0, format); #endif surface = eglCreateWindowSurface(display, config, (EGLNativeWindowType)window, nullptr); if (surface == EGL_NO_SURFACE) { ERROR_LOG(RENDERER, "EGL Error: eglCreateWindowSurface failed: %x", eglGetError()); return false; } #ifndef GLES bool try_full_gl = true; if (!eglBindAPI(EGL_OPENGL_API)) { INFO_LOG(RENDERER, "eglBindAPI(EGL_OPENGL_API) failed: %x", eglGetError()); try_full_gl = false; } if (try_full_gl) { EGLint contextAttrs[] = { EGL_CONTEXT_MAJOR_VERSION, 3, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, EGL_NONE }; context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttrs); if (context != EGL_NO_CONTEXT) { makeCurrent(); if (!gladLoadGL((GLADloadfunc) eglGetProcAddress)) ERROR_LOG(RENDERER, "gladLoadGL() failed"); } } #endif if (context == EGL_NO_CONTEXT) { if (!eglBindAPI(EGL_OPENGL_ES_API)) { ERROR_LOG(RENDERER, "eglBindAPI() failed: %x", eglGetError()); return false; } EGLint contextAttrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2 , EGL_NONE }; context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttrs); if (context == EGL_NO_CONTEXT) { ERROR_LOG(RENDERER, "eglCreateContext() failed: %x", eglGetError()); return false; } makeCurrent(); if (!gladLoadGLES2((GLADloadfunc) eglGetProcAddress)) ERROR_LOG(RENDERER, "gladLoadGLES2() failed"); } } if (!makeCurrent()) { ERROR_LOG(RENDERER, "eglMakeCurrent() failed: %x", eglGetError()); return false; } EGLint w,h; eglQuerySurface(display, surface, EGL_WIDTH, &w); eglQuerySurface(display, surface, EGL_HEIGHT, &h); NOTICE_LOG(RENDERER, "eglQuerySurface: %d - %d", w, h); settings.display.width = w; settings.display.height = h; setSwapInterval(); postInit(); INFO_LOG(RENDERER, "EGL config: %p, %p, %p %dx%d", context, display, surface, w, h); return true; } void EGLGraphicsContext::term() { preTerm(); if (display != EGL_NO_DISPLAY) { eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (context != EGL_NO_CONTEXT) eglDestroyContext(display, context); if (surface != EGL_NO_SURFACE) eglDestroySurface(display, surface); eglTerminate(display); } context = EGL_NO_CONTEXT; surface = EGL_NO_SURFACE; display = EGL_NO_DISPLAY; } void EGLGraphicsContext::swap() { do_swap_automation(); if (swapOnVSync == (settings.input.fastForwardMode || !config::VSync)) setSwapInterval(); eglSwapBuffers(display, surface); } void EGLGraphicsContext::setSwapInterval() { swapOnVSync = (!settings.input.fastForwardMode && config::VSync); int swapInterval; if (settings.display.refreshRate > 60.f) swapInterval = settings.display.refreshRate / 60.f; else swapInterval = 1; eglSwapInterval(display, swapOnVSync ? swapInterval : 0); } #endif // USE_EGL