duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

opengl_context_egl.cpp (18946B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0)
      3 
      4 #include "opengl_context_egl.h"
      5 
      6 #include "common/assert.h"
      7 #include "common/dynamic_library.h"
      8 #include "common/error.h"
      9 #include "common/log.h"
     10 
     11 #include <atomic>
     12 #include <cstring>
     13 #include <optional>
     14 #include <vector>
     15 
     16 Log_SetChannel(OpenGLContext);
     17 
     18 static DynamicLibrary s_egl_library;
     19 static std::atomic_uint32_t s_egl_refcount = 0;
     20 
     21 static bool LoadEGL()
     22 {
     23   // We're not going to be calling this from multiple threads concurrently.
     24   // So, not wrapping this in a mutex should be fine.
     25   if (s_egl_refcount.fetch_add(1, std::memory_order_acq_rel) == 0)
     26   {
     27     DebugAssert(!s_egl_library.IsOpen());
     28 
     29     std::string egl_libname = DynamicLibrary::GetVersionedFilename("libEGL");
     30     INFO_LOG("Loading EGL from {}...", egl_libname);
     31 
     32     Error error;
     33     if (!s_egl_library.Open(egl_libname.c_str(), &error))
     34     {
     35       // Try versioned.
     36       egl_libname = DynamicLibrary::GetVersionedFilename("libEGL", 1);
     37       INFO_LOG("Loading EGL from {}...", egl_libname);
     38       if (!s_egl_library.Open(egl_libname.c_str(), &error))
     39         ERROR_LOG("Failed to load EGL: {}", error.GetDescription());
     40     }
     41   }
     42 
     43   return s_egl_library.IsOpen();
     44 }
     45 
     46 static void UnloadEGL()
     47 {
     48   DebugAssert(s_egl_refcount.load(std::memory_order_acquire) > 0);
     49   if (s_egl_refcount.fetch_sub(1, std::memory_order_acq_rel) == 1)
     50   {
     51     INFO_LOG("Unloading EGL.");
     52     s_egl_library.Close();
     53   }
     54 }
     55 
     56 static bool LoadGLADEGL(EGLDisplay display, Error* error)
     57 {
     58   const int version =
     59     gladLoadEGL(display, [](const char* name) { return (GLADapiproc)s_egl_library.GetSymbolAddress(name); });
     60   if (version == 0)
     61   {
     62     Error::SetStringView(error, "Loading GLAD EGL functions failed");
     63     return false;
     64   }
     65 
     66   DEV_LOG("GLAD EGL Version: {}.{}", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version));
     67   return true;
     68 }
     69 
     70 OpenGLContextEGL::OpenGLContextEGL(const WindowInfo& wi) : OpenGLContext(wi)
     71 {
     72   LoadEGL();
     73 }
     74 
     75 OpenGLContextEGL::~OpenGLContextEGL()
     76 {
     77   DestroySurface();
     78   DestroyContext();
     79   UnloadEGL();
     80 }
     81 
     82 std::unique_ptr<OpenGLContext> OpenGLContextEGL::Create(const WindowInfo& wi, std::span<const Version> versions_to_try,
     83                                                         Error* error)
     84 {
     85   std::unique_ptr<OpenGLContextEGL> context = std::make_unique<OpenGLContextEGL>(wi);
     86   if (!context->Initialize(versions_to_try, error))
     87     return nullptr;
     88 
     89   return context;
     90 }
     91 
     92 bool OpenGLContextEGL::Initialize(std::span<const Version> versions_to_try, Error* error)
     93 {
     94   if (!LoadGLADEGL(EGL_NO_DISPLAY, error))
     95     return false;
     96 
     97   m_display = GetPlatformDisplay(error);
     98   if (m_display == EGL_NO_DISPLAY)
     99     return false;
    100 
    101   int egl_major, egl_minor;
    102   if (!eglInitialize(m_display, &egl_major, &egl_minor))
    103   {
    104     const int gerror = static_cast<int>(eglGetError());
    105     Error::SetStringFmt(error, "eglInitialize() failed: {} (0x{:X})", gerror, gerror);
    106     return false;
    107   }
    108 
    109   DEV_LOG("eglInitialize() version: {}.{}", egl_major, egl_minor);
    110 
    111   // Re-initialize EGL/GLAD.
    112   if (!LoadGLADEGL(m_display, error))
    113     return false;
    114 
    115   if (!GLAD_EGL_KHR_surfaceless_context)
    116     WARNING_LOG("EGL implementation does not support surfaceless contexts, emulating with pbuffers");
    117 
    118   for (const Version& cv : versions_to_try)
    119   {
    120     if (CreateContextAndSurface(cv, nullptr, true))
    121       return true;
    122   }
    123 
    124   Error::SetStringView(error, "Failed to create any context versions");
    125   return false;
    126 }
    127 
    128 EGLDisplay OpenGLContextEGL::GetPlatformDisplay(Error* error)
    129 {
    130   EGLDisplay dpy = TryGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, "EGL_MESA_platform_surfaceless");
    131   if (dpy == EGL_NO_DISPLAY)
    132     dpy = GetFallbackDisplay(error);
    133 
    134   return dpy;
    135 }
    136 
    137 EGLSurface OpenGLContextEGL::CreatePlatformSurface(EGLConfig config, void* win, Error* error)
    138 {
    139   EGLSurface surface = TryCreatePlatformSurface(config, win, error);
    140   if (!surface)
    141     surface = CreateFallbackSurface(config, win, error);
    142   return surface;
    143 }
    144 
    145 EGLDisplay OpenGLContextEGL::TryGetPlatformDisplay(EGLenum platform, const char* platform_ext)
    146 {
    147   const char* extensions_str = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
    148   if (!extensions_str)
    149   {
    150     ERROR_LOG("No extensions supported.");
    151     return EGL_NO_DISPLAY;
    152   }
    153 
    154   EGLDisplay dpy = EGL_NO_DISPLAY;
    155   if (platform_ext && std::strstr(extensions_str, platform_ext))
    156   {
    157     DEV_LOG("Using EGL platform {}.", platform_ext);
    158 
    159     PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display_ext =
    160       (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
    161     if (get_platform_display_ext)
    162     {
    163       dpy = get_platform_display_ext(platform, m_wi.display_connection, nullptr);
    164       m_use_ext_platform_base = (dpy != EGL_NO_DISPLAY);
    165       if (!m_use_ext_platform_base)
    166       {
    167         const EGLint err = eglGetError();
    168         ERROR_LOG("eglGetPlatformDisplayEXT() failed: {} (0x{:X})", err, err);
    169       }
    170     }
    171     else
    172     {
    173       WARNING_LOG("eglGetPlatformDisplayEXT() was not found");
    174     }
    175   }
    176   else
    177   {
    178     WARNING_LOG("{} is not supported.", platform_ext);
    179   }
    180 
    181   return dpy;
    182 }
    183 
    184 EGLSurface OpenGLContextEGL::TryCreatePlatformSurface(EGLConfig config, void* win, Error* error)
    185 {
    186   EGLSurface surface = EGL_NO_SURFACE;
    187   if (m_use_ext_platform_base)
    188   {
    189     PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC create_platform_window_surface_ext =
    190       (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
    191     if (create_platform_window_surface_ext)
    192     {
    193       surface = create_platform_window_surface_ext(m_display, config, win, nullptr);
    194       if (surface == EGL_NO_SURFACE)
    195       {
    196         const EGLint err = eglGetError();
    197         Error::SetStringFmt(error, "eglCreatePlatformWindowSurfaceEXT() failed: {} (0x{:X})", err, err);
    198       }
    199     }
    200     else
    201     {
    202       ERROR_LOG("eglCreatePlatformWindowSurfaceEXT() not found");
    203     }
    204   }
    205 
    206   return surface;
    207 }
    208 
    209 EGLDisplay OpenGLContextEGL::GetFallbackDisplay(Error* error)
    210 {
    211   WARNING_LOG("Using fallback eglGetDisplay() path.");
    212 
    213   EGLDisplay dpy = eglGetDisplay(m_wi.display_connection);
    214   if (dpy == EGL_NO_DISPLAY)
    215   {
    216     const EGLint err = eglGetError();
    217     Error::SetStringFmt(error, "eglGetDisplay() failed: {} (0x{:X})", err, err);
    218   }
    219 
    220   return dpy;
    221 }
    222 
    223 EGLSurface OpenGLContextEGL::CreateFallbackSurface(EGLConfig config, void* win, Error* error)
    224 {
    225   WARNING_LOG("Using fallback eglCreateWindowSurface() path.");
    226 
    227   EGLSurface surface = eglCreateWindowSurface(m_display, config, (EGLNativeWindowType)win, nullptr);
    228   if (surface == EGL_NO_SURFACE)
    229   {
    230     const EGLint err = eglGetError();
    231     Error::SetStringFmt(error, "eglCreateWindowSurface() failed: {} (0x{:X})", err, err);
    232   }
    233 
    234   return surface;
    235 }
    236 
    237 void* OpenGLContextEGL::GetProcAddress(const char* name)
    238 {
    239   return reinterpret_cast<void*>(eglGetProcAddress(name));
    240 }
    241 
    242 bool OpenGLContextEGL::ChangeSurface(const WindowInfo& new_wi)
    243 {
    244   const bool was_current = (eglGetCurrentContext() == m_context);
    245   if (was_current)
    246     eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    247 
    248   if (m_surface != EGL_NO_SURFACE)
    249   {
    250     eglDestroySurface(m_display, m_surface);
    251     m_surface = EGL_NO_SURFACE;
    252   }
    253 
    254   m_wi = new_wi;
    255   if (!CreateSurface())
    256     return false;
    257 
    258   if (was_current && !eglMakeCurrent(m_display, m_surface, m_surface, m_context))
    259   {
    260     ERROR_LOG("Failed to make context current again after surface change");
    261     return false;
    262   }
    263 
    264   return true;
    265 }
    266 
    267 void OpenGLContextEGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/)
    268 {
    269   if (new_surface_width == 0 && new_surface_height == 0)
    270   {
    271     EGLint surface_width, surface_height;
    272     if (eglQuerySurface(m_display, m_surface, EGL_WIDTH, &surface_width) &&
    273         eglQuerySurface(m_display, m_surface, EGL_HEIGHT, &surface_height)) [[likely]]
    274     {
    275       m_wi.surface_width = static_cast<u32>(surface_width);
    276       m_wi.surface_height = static_cast<u32>(surface_height);
    277       return;
    278     }
    279     else
    280     {
    281       ERROR_LOG("eglQuerySurface() failed: 0x{:X}", eglGetError());
    282     }
    283   }
    284 
    285   m_wi.surface_width = new_surface_width;
    286   m_wi.surface_height = new_surface_height;
    287 }
    288 
    289 bool OpenGLContextEGL::SwapBuffers()
    290 {
    291   return eglSwapBuffers(m_display, m_surface);
    292 }
    293 
    294 bool OpenGLContextEGL::IsCurrent() const
    295 {
    296   return m_context && eglGetCurrentContext() == m_context;
    297 }
    298 
    299 bool OpenGLContextEGL::MakeCurrent()
    300 {
    301   if (!eglMakeCurrent(m_display, m_surface, m_surface, m_context)) [[unlikely]]
    302   {
    303     ERROR_LOG("eglMakeCurrent() failed: 0x{:X}", eglGetError());
    304     return false;
    305   }
    306 
    307   return true;
    308 }
    309 
    310 bool OpenGLContextEGL::DoneCurrent()
    311 {
    312   return eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    313 }
    314 
    315 bool OpenGLContextEGL::SupportsNegativeSwapInterval() const
    316 {
    317   return m_supports_negative_swap_interval;
    318 }
    319 
    320 bool OpenGLContextEGL::SetSwapInterval(s32 interval)
    321 {
    322   return eglSwapInterval(m_display, interval);
    323 }
    324 
    325 std::unique_ptr<OpenGLContext> OpenGLContextEGL::CreateSharedContext(const WindowInfo& wi, Error* error)
    326 {
    327   std::unique_ptr<OpenGLContextEGL> context = std::make_unique<OpenGLContextEGL>(wi);
    328   context->m_display = m_display;
    329 
    330   if (!context->CreateContextAndSurface(m_version, m_context, false))
    331   {
    332     Error::SetStringView(error, "Failed to create context/surface");
    333     return nullptr;
    334   }
    335 
    336   return context;
    337 }
    338 
    339 bool OpenGLContextEGL::CreateSurface()
    340 {
    341   if (m_wi.type == WindowInfo::Type::Surfaceless)
    342   {
    343     if (GLAD_EGL_KHR_surfaceless_context)
    344       return true;
    345     else
    346       return CreatePBufferSurface();
    347   }
    348 
    349   Error error;
    350   m_surface = CreatePlatformSurface(m_config, m_wi.window_handle, &error);
    351   if (m_surface == EGL_NO_SURFACE)
    352   {
    353     ERROR_LOG("Failed to create platform surface: {}", error.GetDescription());
    354     return false;
    355   }
    356 
    357   // Some implementations may require the size to be queried at runtime.
    358   EGLint surface_width, surface_height;
    359   if (eglQuerySurface(m_display, m_surface, EGL_WIDTH, &surface_width) &&
    360       eglQuerySurface(m_display, m_surface, EGL_HEIGHT, &surface_height))
    361   {
    362     m_wi.surface_width = static_cast<u32>(surface_width);
    363     m_wi.surface_height = static_cast<u32>(surface_height);
    364   }
    365   else
    366   {
    367     ERROR_LOG("eglQuerySurface() failed: 0x{:X}", eglGetError());
    368   }
    369 
    370   m_wi.surface_format = GetSurfaceTextureFormat();
    371 
    372   return true;
    373 }
    374 
    375 bool OpenGLContextEGL::CreatePBufferSurface()
    376 {
    377   const u32 width = std::max<u32>(m_wi.surface_width, 1);
    378   const u32 height = std::max<u32>(m_wi.surface_height, 1);
    379 
    380   // TODO: Format
    381   EGLint attrib_list[] = {
    382     EGL_WIDTH, static_cast<EGLint>(width), EGL_HEIGHT, static_cast<EGLint>(height), EGL_NONE,
    383   };
    384 
    385   m_surface = eglCreatePbufferSurface(m_display, m_config, attrib_list);
    386   if (!m_surface) [[unlikely]]
    387   {
    388     ERROR_LOG("eglCreatePbufferSurface() failed: {}", eglGetError());
    389     return false;
    390   }
    391 
    392   m_wi.surface_format = GetSurfaceTextureFormat();
    393 
    394   DEV_LOG("Created {}x{} pbuffer surface", width, height);
    395   return true;
    396 }
    397 
    398 bool OpenGLContextEGL::CheckConfigSurfaceFormat(EGLConfig config, GPUTexture::Format format)
    399 {
    400   int red_size, green_size, blue_size, alpha_size;
    401   if (!eglGetConfigAttrib(m_display, config, EGL_RED_SIZE, &red_size) ||
    402       !eglGetConfigAttrib(m_display, config, EGL_GREEN_SIZE, &green_size) ||
    403       !eglGetConfigAttrib(m_display, config, EGL_BLUE_SIZE, &blue_size) ||
    404       !eglGetConfigAttrib(m_display, config, EGL_ALPHA_SIZE, &alpha_size))
    405   {
    406     return false;
    407   }
    408 
    409   switch (format)
    410   {
    411     case GPUTexture::Format::RGBA8:
    412       return (red_size == 8 && green_size == 8 && blue_size == 8 && alpha_size == 8);
    413 
    414     case GPUTexture::Format::RGB565:
    415       return (red_size == 5 && green_size == 6 && blue_size == 5);
    416 
    417     case GPUTexture::Format::RGBA5551:
    418       return (red_size == 5 && green_size == 5 && blue_size == 5 && alpha_size == 1);
    419 
    420     case GPUTexture::Format::Unknown:
    421       return true;
    422 
    423     default:
    424       return false;
    425   }
    426 }
    427 
    428 GPUTexture::Format OpenGLContextEGL::GetSurfaceTextureFormat() const
    429 {
    430   int red_size = 0, green_size = 0, blue_size = 0, alpha_size = 0;
    431   eglGetConfigAttrib(m_display, m_config, EGL_RED_SIZE, &red_size);
    432   eglGetConfigAttrib(m_display, m_config, EGL_GREEN_SIZE, &green_size);
    433   eglGetConfigAttrib(m_display, m_config, EGL_BLUE_SIZE, &blue_size);
    434   eglGetConfigAttrib(m_display, m_config, EGL_ALPHA_SIZE, &alpha_size);
    435 
    436   if (red_size == 5 && green_size == 6 && blue_size == 5)
    437   {
    438     return GPUTexture::Format::RGB565;
    439   }
    440   else if (red_size == 5 && green_size == 5 && blue_size == 5 && alpha_size == 1)
    441   {
    442     return GPUTexture::Format::RGBA5551;
    443   }
    444   else if (red_size == 8 && green_size == 8 && blue_size == 8 && alpha_size == 8)
    445   {
    446     return GPUTexture::Format::RGBA8;
    447   }
    448   else
    449   {
    450     ERROR_LOG("Unknown surface format: R={}, G={}, B={}, A={}", red_size, green_size, blue_size, alpha_size);
    451     return GPUTexture::Format::RGBA8;
    452   }
    453 }
    454 
    455 void OpenGLContextEGL::DestroyContext()
    456 {
    457   if (eglGetCurrentContext() == m_context)
    458     eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    459 
    460   if (m_context != EGL_NO_CONTEXT)
    461   {
    462     eglDestroyContext(m_display, m_context);
    463     m_context = EGL_NO_CONTEXT;
    464   }
    465 }
    466 
    467 void OpenGLContextEGL::DestroySurface()
    468 {
    469   if (eglGetCurrentSurface(EGL_DRAW) == m_surface)
    470     eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    471 
    472   if (m_surface != EGL_NO_SURFACE)
    473   {
    474     eglDestroySurface(m_display, m_surface);
    475     m_surface = EGL_NO_SURFACE;
    476   }
    477 }
    478 
    479 bool OpenGLContextEGL::CreateContext(const Version& version, EGLContext share_context)
    480 {
    481   DEV_LOG("Trying version {}.{} ({})", version.major_version, version.minor_version,
    482           version.profile == OpenGLContext::Profile::ES ?
    483             "ES" :
    484             (version.profile == OpenGLContext::Profile::Core ? "Core" : "None"));
    485   int surface_attribs[16] = {
    486     EGL_RENDERABLE_TYPE,
    487     (version.profile == Profile::ES) ?
    488       ((version.major_version >= 3) ? EGL_OPENGL_ES3_BIT :
    489                                       ((version.major_version == 2) ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES_BIT)) :
    490       EGL_OPENGL_BIT,
    491     EGL_SURFACE_TYPE,
    492     (m_wi.type != WindowInfo::Type::Surfaceless) ? EGL_WINDOW_BIT : 0,
    493   };
    494   int nsurface_attribs = 4;
    495 
    496   const GPUTexture::Format format = m_wi.surface_format;
    497   if (format == GPUTexture::Format::Unknown)
    498   {
    499     WARNING_LOG("Surface format not specified, assuming RGBA8.");
    500     m_wi.surface_format = GPUTexture::Format::RGBA8;
    501   }
    502 
    503   switch (m_wi.surface_format)
    504   {
    505     case GPUTexture::Format::RGBA8:
    506       surface_attribs[nsurface_attribs++] = EGL_RED_SIZE;
    507       surface_attribs[nsurface_attribs++] = 8;
    508       surface_attribs[nsurface_attribs++] = EGL_GREEN_SIZE;
    509       surface_attribs[nsurface_attribs++] = 8;
    510       surface_attribs[nsurface_attribs++] = EGL_BLUE_SIZE;
    511       surface_attribs[nsurface_attribs++] = 8;
    512       surface_attribs[nsurface_attribs++] = EGL_ALPHA_SIZE;
    513       surface_attribs[nsurface_attribs++] = 8;
    514       break;
    515 
    516     case GPUTexture::Format::RGB565:
    517       surface_attribs[nsurface_attribs++] = EGL_RED_SIZE;
    518       surface_attribs[nsurface_attribs++] = 5;
    519       surface_attribs[nsurface_attribs++] = EGL_GREEN_SIZE;
    520       surface_attribs[nsurface_attribs++] = 6;
    521       surface_attribs[nsurface_attribs++] = EGL_BLUE_SIZE;
    522       surface_attribs[nsurface_attribs++] = 5;
    523       break;
    524 
    525     case GPUTexture::Format::Unknown:
    526       break;
    527 
    528     default:
    529       UnreachableCode();
    530       break;
    531   }
    532 
    533   surface_attribs[nsurface_attribs++] = EGL_NONE;
    534   surface_attribs[nsurface_attribs++] = 0;
    535 
    536   EGLint num_configs;
    537   if (!eglChooseConfig(m_display, surface_attribs, nullptr, 0, &num_configs) || num_configs == 0)
    538   {
    539     ERROR_LOG("eglChooseConfig() failed: 0x{:x}", static_cast<unsigned>(eglGetError()));
    540     return false;
    541   }
    542 
    543   std::vector<EGLConfig> configs(static_cast<u32>(num_configs));
    544   if (!eglChooseConfig(m_display, surface_attribs, configs.data(), num_configs, &num_configs))
    545   {
    546     ERROR_LOG("eglChooseConfig() failed: 0x{:x}", static_cast<unsigned>(eglGetError()));
    547     return false;
    548   }
    549   configs.resize(static_cast<u32>(num_configs));
    550 
    551   std::optional<EGLConfig> config;
    552   for (EGLConfig check_config : configs)
    553   {
    554     if (CheckConfigSurfaceFormat(check_config, m_wi.surface_format))
    555     {
    556       config = check_config;
    557       break;
    558     }
    559   }
    560 
    561   if (!config.has_value())
    562   {
    563     WARNING_LOG("No EGL configs matched exactly, using first.");
    564     config = configs.front();
    565   }
    566 
    567   int attribs[8];
    568   int nattribs = 0;
    569   if (version.profile != Profile::NoProfile)
    570   {
    571     attribs[nattribs++] = EGL_CONTEXT_MAJOR_VERSION;
    572     attribs[nattribs++] = version.major_version;
    573     attribs[nattribs++] = EGL_CONTEXT_MINOR_VERSION;
    574     attribs[nattribs++] = version.minor_version;
    575   }
    576   attribs[nattribs++] = EGL_NONE;
    577   attribs[nattribs++] = 0;
    578 
    579   if (!eglBindAPI((version.profile == Profile::ES) ? EGL_OPENGL_ES_API : EGL_OPENGL_API))
    580   {
    581     ERROR_LOG("eglBindAPI({}) failed", (version.profile == Profile::ES) ? "EGL_OPENGL_ES_API" : "EGL_OPENGL_API");
    582     return false;
    583   }
    584 
    585   m_context = eglCreateContext(m_display, config.value(), share_context, attribs);
    586   if (!m_context)
    587   {
    588     ERROR_LOG("eglCreateContext() failed: 0x{:x}", static_cast<unsigned>(eglGetError()));
    589     return false;
    590   }
    591 
    592   INFO_LOG("Got version {}.{} ({})", version.major_version, version.minor_version,
    593            version.profile == OpenGLContext::Profile::ES ?
    594              "ES" :
    595              (version.profile == OpenGLContext::Profile::Core ? "Core" : "None"));
    596 
    597   EGLint min_swap_interval, max_swap_interval;
    598   m_supports_negative_swap_interval = false;
    599   if (eglGetConfigAttrib(m_display, config.value(), EGL_MIN_SWAP_INTERVAL, &min_swap_interval) &&
    600       eglGetConfigAttrib(m_display, config.value(), EGL_MAX_SWAP_INTERVAL, &max_swap_interval))
    601   {
    602     VERBOSE_LOG("EGL_MIN_SWAP_INTERVAL = {}", min_swap_interval);
    603     VERBOSE_LOG("EGL_MAX_SWAP_INTERVAL = {}", max_swap_interval);
    604     m_supports_negative_swap_interval = (min_swap_interval <= -1);
    605   }
    606 
    607   INFO_LOG("Negative swap interval/tear-control is {}supported", m_supports_negative_swap_interval ? "" : "NOT ");
    608 
    609   m_config = config.value();
    610   m_version = version;
    611   return true;
    612 }
    613 
    614 bool OpenGLContextEGL::CreateContextAndSurface(const Version& version, EGLContext share_context, bool make_current)
    615 {
    616   if (!CreateContext(version, share_context))
    617     return false;
    618 
    619   if (!CreateSurface())
    620   {
    621     ERROR_LOG("Failed to create surface for context");
    622     eglDestroyContext(m_display, m_context);
    623     m_context = EGL_NO_CONTEXT;
    624     return false;
    625   }
    626 
    627   if (make_current && !eglMakeCurrent(m_display, m_surface, m_surface, m_context))
    628   {
    629     ERROR_LOG("eglMakeCurrent() failed: 0x{:X}", eglGetError());
    630     if (m_surface != EGL_NO_SURFACE)
    631     {
    632       eglDestroySurface(m_display, m_surface);
    633       m_surface = EGL_NO_SURFACE;
    634     }
    635     eglDestroyContext(m_display, m_context);
    636     m_context = EGL_NO_CONTEXT;
    637     return false;
    638   }
    639 
    640   return true;
    641 }