CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/SDL/SDLGLGraphicsContext.cpp
Views: 1401
#include <vector>12#include "SDLGLGraphicsContext.h"34#include "Common/GPU/OpenGL/GLFeatures.h"5#include "Common/GPU/thin3d_create.h"67#include "Common/System/NativeApp.h"8#include "Common/System/System.h"9#include "Common/System/Display.h"10#include "Core/Config.h"11#include "Core/ConfigValues.h"12#include "Core/System.h"1314#if defined(USING_EGL)15#include "EGL/egl.h"16#endif1718class GLRenderManager;1920#if defined(USING_EGL)2122// TODO: Move these into the class.23static EGLDisplay g_eglDisplay = EGL_NO_DISPLAY;24static EGLContext g_eglContext = nullptr;25static EGLSurface g_eglSurface = nullptr;26static EGLNativeDisplayType g_Display = nullptr;27static bool g_XDisplayOpen = false;28static EGLNativeWindowType g_Window = (EGLNativeWindowType)nullptr;29static bool useEGLSwap = false;3031int CheckEGLErrors(const char *file, int line) {32EGLenum error;33const char *errortext = "unknown";34error = eglGetError();35switch (error)36{37case EGL_SUCCESS: case 0: return 0;38case EGL_NOT_INITIALIZED: errortext = "EGL_NOT_INITIALIZED"; break;39case EGL_BAD_ACCESS: errortext = "EGL_BAD_ACCESS"; break;40case EGL_BAD_ALLOC: errortext = "EGL_BAD_ALLOC"; break;41case EGL_BAD_ATTRIBUTE: errortext = "EGL_BAD_ATTRIBUTE"; break;42case EGL_BAD_CONTEXT: errortext = "EGL_BAD_CONTEXT"; break;43case EGL_BAD_CONFIG: errortext = "EGL_BAD_CONFIG"; break;44case EGL_BAD_CURRENT_SURFACE: errortext = "EGL_BAD_CURRENT_SURFACE"; break;45case EGL_BAD_DISPLAY: errortext = "EGL_BAD_DISPLAY"; break;46case EGL_BAD_SURFACE: errortext = "EGL_BAD_SURFACE"; break;47case EGL_BAD_MATCH: errortext = "EGL_BAD_MATCH"; break;48case EGL_BAD_PARAMETER: errortext = "EGL_BAD_PARAMETER"; break;49case EGL_BAD_NATIVE_PIXMAP: errortext = "EGL_BAD_NATIVE_PIXMAP"; break;50case EGL_BAD_NATIVE_WINDOW: errortext = "EGL_BAD_NATIVE_WINDOW"; break;51default: errortext = "unknown"; break;52}53printf( "ERROR: EGL Error %s detected in file %s at line %d (0x%X)\n", errortext, file, line, error );54return 1;55}5657#define EGL_ERROR(str, check) { \58if (check) CheckEGLErrors( __FILE__, __LINE__ ); \59printf("EGL ERROR: " str "\n"); \60return 1; \61}6263static bool EGL_OpenInit() {64if ((g_eglDisplay = eglGetDisplay(g_Display)) == EGL_NO_DISPLAY) {65EGL_ERROR("Unable to create EGL display.", true);66return false;67}68if (eglInitialize(g_eglDisplay, NULL, NULL) != EGL_TRUE) {69EGL_ERROR("Unable to initialize EGL display.", true);70eglTerminate(g_eglDisplay);71g_eglDisplay = EGL_NO_DISPLAY;72return false;73}7475return true;76}7778static int8_t EGL_Open(SDL_Window *window) {79#if defined(USING_FBDEV)80g_Display = (EGLNativeDisplayType)nullptr;81g_Window = (EGLNativeWindowType)nullptr;82#elif defined(__APPLE__)83g_Display = (EGLNativeDisplayType)XOpenDisplay(nullptr);84g_XDisplayOpen = g_Display != nullptr;85if (!g_XDisplayOpen)86EGL_ERROR("Unable to get display!", false);87g_Window = (EGLNativeWindowType)nullptr;88#else89// Get the SDL window native handle90SDL_SysWMinfo sysInfo{};91SDL_VERSION(&sysInfo.version);92if (!SDL_GetWindowWMInfo(window, &sysInfo)) {93printf("ERROR: Unable to retrieve native window handle\n");94g_Display = (EGLNativeDisplayType)XOpenDisplay(nullptr);95g_XDisplayOpen = g_Display != nullptr;96if (!g_XDisplayOpen)97EGL_ERROR("Unable to get display!", false);98g_Window = (EGLNativeWindowType)nullptr;99} else {100switch (sysInfo.subsystem) {101case SDL_SYSWM_X11:102g_Display = (EGLNativeDisplayType)sysInfo.info.x11.display;103g_Window = (EGLNativeWindowType)sysInfo.info.x11.window;104break;105#if defined(SDL_VIDEO_DRIVER_DIRECTFB)106case SDL_SYSWM_DIRECTFB:107g_Display = (EGLNativeDisplayType)EGL_DEFAULT_DISPLAY;108g_Window = (EGLNativeWindowType)sysInfo.info.dfb.surface;109break;110#endif111#if SDL_VERSION_ATLEAST(2, 0, 2) && defined(SDL_VIDEO_DRIVER_WAYLAND)112case SDL_SYSWM_WAYLAND:113g_Display = (EGLNativeDisplayType)sysInfo.info.wl.display;114g_Window = (EGLNativeWindowType)sysInfo.info.wl.shell_surface;115break;116#endif117#if SDL_VERSION_ATLEAST(2, 0, 5) && defined(SDL_VIDEO_DRIVER_VIVANTE)118case SDL_SYSWM_VIVANTE:119g_Display = (EGLNativeDisplayType)sysInfo.info.vivante.display;120g_Window = (EGLNativeWindowType)sysInfo.info.vivante.window;121break;122#endif123}124125if (!EGL_OpenInit()) {126// Let's try again with X11.127g_Display = (EGLNativeDisplayType)XOpenDisplay(nullptr);128g_XDisplayOpen = g_Display != nullptr;129if (!g_XDisplayOpen)130EGL_ERROR("Unable to get display!", false);131g_Window = (EGLNativeWindowType)nullptr;132}133}134135#endif136if (g_eglDisplay == EGL_NO_DISPLAY)137EGL_OpenInit();138return g_eglDisplay == EGL_NO_DISPLAY ? 1 : 0;139}140141#ifndef EGL_OPENGL_ES3_BIT_KHR142#define EGL_OPENGL_ES3_BIT_KHR (1 << 6)143#endif144145EGLConfig EGL_FindConfig(int *contextVersion) {146std::vector<EGLConfig> configs;147EGLint numConfigs = 0;148149EGLBoolean result = eglGetConfigs(g_eglDisplay, nullptr, 0, &numConfigs);150if (result != EGL_TRUE || numConfigs == 0) {151return nullptr;152}153154configs.resize(numConfigs);155result = eglGetConfigs(g_eglDisplay, &configs[0], numConfigs, &numConfigs);156if (result != EGL_TRUE || numConfigs == 0) {157return nullptr;158}159160// Mali (ARM) seems to have compositing issues with alpha backbuffers.161// EGL_TRANSPARENT_TYPE doesn't help.162const char *vendorName = eglQueryString(g_eglDisplay, EGL_VENDOR);163const bool avoidAlphaGLES = vendorName && !strcmp(vendorName, "ARM");164165EGLConfig best = nullptr;166int bestScore = 0;167int bestContextVersion = 0;168for (const EGLConfig &config : configs) {169auto readConfig = [&](EGLint attr) -> EGLint {170EGLint val = 0;171eglGetConfigAttrib(g_eglDisplay, config, attr, &val);172return val;173};174175// We don't want HDR modes with more than 8 bits per component.176// But let's assume some color is better than no color at all.177auto readConfigMax = [&](EGLint attr, EGLint m, EGLint def = 1) -> EGLint {178EGLint val = readConfig(attr);179return val > m ? def : val;180};181182int colorScore = readConfigMax(EGL_RED_SIZE, 8) + readConfigMax(EGL_BLUE_SIZE, 8) + readConfigMax(EGL_GREEN_SIZE, 8);183int alphaScore = readConfigMax(EGL_ALPHA_SIZE, 8);184int depthScore = readConfig(EGL_DEPTH_SIZE);185int levelScore = readConfig(EGL_LEVEL) == 0 ? 100 : 0;186int samplesScore = readConfig(EGL_SAMPLES) == 0 ? 100 : 0;187int sampleBufferScore = readConfig(EGL_SAMPLE_BUFFERS) == 0 ? 100 : 0;188int stencilScore = readConfig(EGL_STENCIL_SIZE);189int transparentScore = readConfig(EGL_TRANSPARENT_TYPE) == EGL_NONE ? 50 : 0;190191EGLint caveat = readConfig(EGL_CONFIG_CAVEAT);192// Let's assume that non-conformant configs aren't so awful.193int caveatScore = caveat == EGL_NONE ? 100 : (caveat == EGL_NON_CONFORMANT_CONFIG ? 95 : 0);194195#ifndef USING_FBDEV196EGLint surfaceType = readConfig(EGL_SURFACE_TYPE);197// Only try a non-Window config in the worst case when there are only non-Window configs.198int surfaceScore = (surfaceType & EGL_WINDOW_BIT) ? 1000 : 0;199#endif200201EGLint renderable = readConfig(EGL_RENDERABLE_TYPE);202bool renderableGLES3 = (renderable & EGL_OPENGL_ES3_BIT_KHR) != 0;203bool renderableGLES2 = (renderable & EGL_OPENGL_ES2_BIT) != 0;204bool renderableGL = (renderable & EGL_OPENGL_BIT) != 0;205#ifdef USING_GLES2206int renderableScoreGLES = renderableGLES3 ? 100 : (renderableGLES2 ? 80 : 0);207int renderableScoreGL = 0;208#else209int renderableScoreGLES = 0;210int renderableScoreGL = renderableGL ? 100 : (renderableGLES3 ? 80 : 0);211#endif212213if (avoidAlphaGLES && renderableScoreGLES > 0) {214alphaScore = 8 - alphaScore;215}216217int score = 0;218// Here's a good place to play with the weights to pick a better config.219score += colorScore * 10 + alphaScore * 2;220score += depthScore * 5 + stencilScore;221score += levelScore + samplesScore + sampleBufferScore + transparentScore;222score += caveatScore + renderableScoreGLES + renderableScoreGL;223224#ifndef USING_FBDEV225score += surfaceScore;226#endif227228if (score > bestScore) {229bestScore = score;230best = config;231bestContextVersion = renderableGLES3 ? 3 : (renderableGLES2 ? 2 : 0);232}233}234235*contextVersion = bestContextVersion;236return best;237}238239int8_t EGL_Init(SDL_Window *window) {240int contextVersion = 0;241EGLConfig eglConfig = EGL_FindConfig(&contextVersion);242if (!eglConfig) {243EGL_ERROR("Unable to find a usable EGL config.", true);244return 1;245}246247EGLint contextAttributes[] = {248EGL_CONTEXT_CLIENT_VERSION, contextVersion,249EGL_NONE,250};251if (contextVersion == 0) {252contextAttributes[0] = EGL_NONE;253}254255g_eglContext = eglCreateContext(g_eglDisplay, eglConfig, nullptr, contextAttributes);256if (g_eglContext == EGL_NO_CONTEXT) {257EGL_ERROR("Unable to create GLES context!", true);258return 1;259}260261g_eglSurface = eglCreateWindowSurface(g_eglDisplay, eglConfig, g_Window, nullptr);262if (g_eglSurface == EGL_NO_SURFACE) {263EGL_ERROR("Unable to create EGL surface!", true);264return 1;265}266267if (eglMakeCurrent(g_eglDisplay, g_eglSurface, g_eglSurface, g_eglContext) != EGL_TRUE) {268EGL_ERROR("Unable to make GLES context current.", true);269return 1;270}271272return 0;273}274275void EGL_Close() {276if (g_eglDisplay != EGL_NO_DISPLAY) {277eglMakeCurrent(g_eglDisplay, NULL, NULL, EGL_NO_CONTEXT);278if (g_eglContext != NULL) {279eglDestroyContext(g_eglDisplay, g_eglContext);280}281if (g_eglSurface != NULL) {282eglDestroySurface(g_eglDisplay, g_eglSurface);283}284eglTerminate(g_eglDisplay);285g_eglDisplay = EGL_NO_DISPLAY;286}287if (g_Display != nullptr) {288#if !defined(USING_FBDEV)289if (g_XDisplayOpen)290XCloseDisplay((Display *)g_Display);291#endif292g_XDisplayOpen = false;293g_Display = nullptr;294}295g_eglSurface = NULL;296g_eglContext = NULL;297}298299#endif // USING_EGL300301bool SDLGLGraphicsContext::InitFromRenderThread(std::string *errorMessage) {302bool retval = GraphicsContext::InitFromRenderThread(errorMessage);303// HACK: Ensure that the swap interval is set after context creation (needed for kmsdrm)304SDL_GL_SetSwapInterval(1);305return retval;306}307308// Returns 0 on success.309int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int w, int h, int mode, std::string *error_message) {310struct GLVersionPair {311int major;312int minor;313};314GLVersionPair attemptVersions[] = {315#ifdef USING_GLES2316{3, 2}, {3, 1}, {3, 0}, {2, 0},317#else318{4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0},319{3, 3}, {3, 2}, {3, 1}, {3, 0},320#endif321};322323// We start hidden because we have to try several windows.324// On Mac, full screen animates so each attempt is slow.325mode |= SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN;326327SDL_GLContext glContext = nullptr;328for (size_t i = 0; i < ARRAY_SIZE(attemptVersions); ++i) {329const auto &ver = attemptVersions[i];330// Make sure to request a somewhat modern GL context at least - the331// latest supported by MacOS X (really, really sad...)332SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, ver.major);333SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, ver.minor);334#ifdef USING_GLES2335SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);336SetGLCoreContext(false);337#else338SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);339SetGLCoreContext(true);340#endif341342window = SDL_CreateWindow("PPSSPP", x, y, w, h, mode);343if (!window) {344// Definitely don't shutdown here: we'll keep trying more GL versions.345fprintf(stderr, "SDL_CreateWindow failed for GL %d.%d: %s\n", ver.major, ver.minor, SDL_GetError());346// Skip the DestroyWindow.347continue;348}349350glContext = SDL_GL_CreateContext(window);351if (glContext != nullptr) {352// Victory, got one.353break;354}355356// Let's keep trying. To be safe, destroy the window - docs say needed to change profile.357// in practice, it doesn't seem to matter, but maybe it differs by platform.358SDL_DestroyWindow(window);359}360361if (glContext == nullptr) {362SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);363SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 0);364SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);365SetGLCoreContext(false);366367window = SDL_CreateWindow("PPSSPP", x, y, w, h, mode);368if (window == nullptr) {369NativeShutdown();370fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError());371SDL_Quit();372return 2;373}374375glContext = SDL_GL_CreateContext(window);376if (glContext == nullptr) {377// OK, now we really have tried everything.378NativeShutdown();379fprintf(stderr, "SDL_GL_CreateContext failed: %s\n", SDL_GetError());380SDL_Quit();381return 2;382}383}384385// At this point, we have a window that we can show finally.386SDL_ShowWindow(window);387388#ifdef USING_EGL389if (EGL_Open(window) != 0) {390printf("EGL_Open() failed\n");391} else if (EGL_Init(window) != 0) {392printf("EGL_Init() failed\n");393} else {394useEGLSwap = true;395}396#endif397398#ifndef USING_GLES2399// Some core profile drivers elide certain extensions from GL_EXTENSIONS/etc.400// glewExperimental allows us to force GLEW to search for the pointers anyway.401if (gl_extensions.IsCoreContext) {402glewExperimental = true;403}404GLenum glew_err = glewInit();405// glx is not required, igore.406if (glew_err != GLEW_OK && glew_err != GLEW_ERROR_NO_GLX_DISPLAY) {407printf("Failed to initialize glew!\n");408return 1;409}410// Unfortunately, glew will generate an invalid enum error, ignore.411if (gl_extensions.IsCoreContext)412glGetError();413414if (GLEW_VERSION_2_0) {415printf("OpenGL 2.0 or higher.\n");416} else {417printf("Sorry, this program requires OpenGL 2.0.\n");418return 1;419}420#endif421422// Finally we can do the regular initialization.423CheckGLExtensions();424draw_ = Draw::T3DCreateGLContext(true);425renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);426renderManager_->SetInflightFrames(g_Config.iInflightFrames);427SetGPUBackend(GPUBackend::OPENGL);428bool success = draw_->CreatePresets();429_assert_(success);430renderManager_->SetSwapFunction([&]() {431#ifdef USING_EGL432if (useEGLSwap)433eglSwapBuffers(g_eglDisplay, g_eglSurface);434else435SDL_GL_SwapWindow(window_);436#else437SDL_GL_SwapWindow(window_);438#endif439});440441renderManager_->SetSwapIntervalFunction([&](int interval) {442INFO_LOG(Log::G3D, "SDL SwapInterval: %d", interval);443SDL_GL_SetSwapInterval(interval);444});445446window_ = window;447return 0;448}449450void SDLGLGraphicsContext::ShutdownFromRenderThread() {451delete draw_;452draw_ = nullptr;453renderManager_ = nullptr;454455#ifdef USING_EGL456EGL_Close();457#endif458SDL_GL_DeleteContext(glContext);459glContext = nullptr;460window_ = nullptr;461}462463464