/* 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" #ifdef __ANDROID__ #include // requires ndk r5 or newer #endif #ifdef TARGET_PANDORA #include #include #include #include #ifndef FBIO_WAITFORVSYNC #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) #endif #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() { //try to get a display display = eglGetDisplay(nativeDisplay); //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; } if (surface == EGL_NO_SURFACE) { EGLint pi32ConfigAttribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_DEPTH_SIZE, 24, EGL_STENCIL_SIZE, 8, EGL_NONE }; int num_config; EGLConfig config; if (!eglChooseConfig(display, pi32ConfigAttribs, &config, 1, &num_config) || (num_config != 1)) { // Fall back to non preserved swap buffers EGLint pi32ConfigFallbackAttribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_DEPTH_SIZE, 24, EGL_STENCIL_SIZE, 8, EGL_NONE }; if (!eglChooseConfig(display, pi32ConfigFallbackAttribs, &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 *)nativeWindow, 0, 0, format); #endif surface = eglCreateWindowSurface(display, config, nativeWindow, NULL); 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_KHR, 3, EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR, EGL_NONE }; context = eglCreateContext(display, config, NULL, contextAttrs); if (context != EGL_NO_CONTEXT) { MakeCurrent(); if (gl3wInit()) ERROR_LOG(RENDERER, "gl3wInit() 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, NULL, contextAttrs); if (context == EGL_NO_CONTEXT) { ERROR_LOG(RENDERER, "eglCreateContext() failed: %x", eglGetError()); return false; } #ifdef GLES // EGL only supports runtime loading with android? TDB load_gles_symbols(); #else MakeCurrent(); if (gl3wInit()) INFO_LOG(RENDERER, "gl3wInit() failed"); #endif } } else if (glGetError == NULL) { // Needed when the context is not created here (Android java mode, iOS) // Broken atm load_gles_symbols(); } 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); screen_width = w; screen_height = h; // Required when doing partial redraws swap_buffer_preserved = true; if (!eglSurfaceAttrib(display, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) { INFO_LOG(RENDERER, "Swap buffers are not preserved. Last frame copy enabled"); swap_buffer_preserved = false; } #ifdef TARGET_PANDORA fbdev = open("/dev/fb0", O_RDONLY); #else eglSwapInterval(display, 1); #endif PostInit(); INFO_LOG(RENDERER, "EGL config: %p, %p, %p %dx%d", context, display, surface, w, h); return true; } void EGLGraphicsContext::Term() { PreTerm(); eglMakeCurrent(display, NULL, NULL, EGL_NO_CONTEXT); if (context != EGL_NO_CONTEXT) eglDestroyContext(display, context); if (surface != EGL_NO_SURFACE) eglDestroySurface(display, surface); #ifdef TARGET_PANDORA if (display != EGL_NO_DISPLAY) eglTerminate(display); if (fbdev >= 0) close(fbdev); fbdev = -1; #endif context = EGL_NO_CONTEXT; surface = EGL_NO_SURFACE; display = EGL_NO_DISPLAY; } void EGLGraphicsContext::Swap() { #ifdef TEST_AUTOMATION do_swap_automation(); #endif #if 0 && defined(TARGET_PANDORA) if (fbdev >= 0) { int arg = 0; ioctl(fbdev,FBIO_WAITFORVSYNC,&arg); } #endif eglSwapBuffers(display, surface); } #endif // USE_EGL