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.cpp (7933B)


      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.h"
      5 #include "opengl_loader.h"
      6 
      7 #include "common/error.h"
      8 #include "common/log.h"
      9 
     10 #include <cstdio>
     11 #include <cstdlib>
     12 #ifdef __APPLE__
     13 #include <stdlib.h>
     14 #else
     15 #include <malloc.h>
     16 #endif
     17 
     18 #if defined(_WIN32) && !defined(_M_ARM64)
     19 #include "opengl_context_wgl.h"
     20 #elif defined(__APPLE__)
     21 #include "opengl_context_agl.h"
     22 #elif defined(__ANDROID__)
     23 #include "opengl_context_egl_android.h"
     24 #else
     25 #ifdef ENABLE_EGL
     26 #ifdef ENABLE_WAYLAND
     27 #include "opengl_context_egl_wayland.h"
     28 #endif
     29 #ifdef ENABLE_X11
     30 #include "opengl_context_egl_x11.h"
     31 #endif
     32 #endif
     33 #endif
     34 
     35 Log_SetChannel(OpenGLContext);
     36 
     37 static bool ShouldPreferESContext()
     38 {
     39 #if defined(__ANDROID__)
     40   return true;
     41 #elif !defined(_MSC_VER)
     42   const char* value = std::getenv("PREFER_GLES_CONTEXT");
     43   return (value && std::strcmp(value, "1") == 0);
     44 #else
     45   char buffer[2] = {};
     46   size_t buffer_size = sizeof(buffer);
     47   getenv_s(&buffer_size, buffer, "PREFER_GLES_CONTEXT");
     48   return (std::strcmp(buffer, "1") == 0);
     49 #endif
     50 }
     51 
     52 static void DisableBrokenExtensions(const char* gl_vendor, const char* gl_renderer, const char* gl_version)
     53 {
     54   if (std::strstr(gl_vendor, "ARM"))
     55   {
     56     // GL_{EXT,OES}_copy_image seem to be implemented on the CPU in the Mali drivers...
     57     // Older drivers don't implement timer queries correctly either.
     58     int gl_major_version, gl_minor_version, unused_version, major_version, patch_version;
     59     if ((std::sscanf(gl_version, "OpenGL ES %d.%d v%d.r%dp%d", &gl_major_version, &gl_minor_version, &unused_version,
     60                      &major_version, &patch_version) == 5 &&
     61          gl_major_version >= 3 && gl_minor_version >= 2 && major_version >= 32) ||
     62         (std::sscanf(gl_version, "OpenGL ES %d.%d v%d.g%dp%d", &gl_major_version, &gl_minor_version, &unused_version,
     63                      &major_version, &patch_version) == 5 &&
     64          gl_major_version >= 3 && gl_minor_version >= 2 && major_version > 0))
     65     {
     66       // r32p0 and beyond seem okay.
     67       // Log_VerbosePrint("Keeping copy_image for driver version '%s'", gl_version);
     68 
     69       // Framebuffer blits still end up faster.
     70       VERBOSE_LOG("Newer Mali driver detected, disabling GL_{EXT,OES}_copy_image.");
     71       GLAD_GL_EXT_copy_image = 0;
     72       GLAD_GL_OES_copy_image = 0;
     73     }
     74     else
     75     {
     76       VERBOSE_LOG("Older Mali driver detected, disabling GL_{EXT,OES}_copy_image, disjoint_timer_query.");
     77       GLAD_GL_EXT_copy_image = 0;
     78       GLAD_GL_OES_copy_image = 0;
     79       GLAD_GL_EXT_disjoint_timer_query = 0;
     80     }
     81   }
     82   else if (std::strstr(gl_vendor, "Qualcomm") && std::strstr(gl_renderer, "Adreno"))
     83   {
     84     // Framebuffer fetch appears to be broken in drivers ?? >= 464 < 502.
     85     int gl_major_version = 0, gl_minor_version = 0, major_version = 0;
     86     if ((std::sscanf(gl_version, "OpenGL ES %d.%d V@%d", &gl_major_version, &gl_minor_version, &major_version) == 3 &&
     87          gl_major_version >= 3 && gl_minor_version >= 2 && major_version < 502))
     88     {
     89       VERBOSE_LOG("Disabling GL_EXT_shader_framebuffer_fetch on Adreno version {}", major_version);
     90       GLAD_GL_EXT_shader_framebuffer_fetch = 0;
     91       GLAD_GL_ARM_shader_framebuffer_fetch = 0;
     92     }
     93     else
     94     {
     95       VERBOSE_LOG("Keeping GL_EXT_shader_framebuffer_fetch on Adreno version {}", major_version);
     96     }
     97   }
     98 
     99   // If we're missing GLES 3.2, but have OES_draw_elements_base_vertex, redirect the function pointers.
    100   if (!glad_glDrawElementsBaseVertex && GLAD_GL_OES_draw_elements_base_vertex && !GLAD_GL_ES_VERSION_3_2)
    101   {
    102     glad_glDrawElementsBaseVertex = glad_glDrawElementsBaseVertexOES;
    103     glad_glDrawRangeElementsBaseVertex = glad_glDrawRangeElementsBaseVertexOES;
    104     glad_glDrawElementsInstancedBaseVertex = glad_glDrawElementsInstancedBaseVertexOES;
    105   }
    106 }
    107 
    108 OpenGLContext::OpenGLContext(const WindowInfo& wi) : m_wi(wi)
    109 {
    110 }
    111 
    112 OpenGLContext::~OpenGLContext() = default;
    113 
    114 std::unique_ptr<OpenGLContext> OpenGLContext::Create(const WindowInfo& wi, Error* error)
    115 {
    116   static constexpr std::array<Version, 14> vlist = {{{Profile::Core, 4, 6},
    117                                                      {Profile::Core, 4, 5},
    118                                                      {Profile::Core, 4, 4},
    119                                                      {Profile::Core, 4, 3},
    120                                                      {Profile::Core, 4, 2},
    121                                                      {Profile::Core, 4, 1},
    122                                                      {Profile::Core, 4, 0},
    123                                                      {Profile::Core, 3, 3},
    124                                                      {Profile::Core, 3, 2},
    125                                                      {Profile::Core, 3, 1},
    126                                                      {Profile::Core, 3, 0},
    127                                                      {Profile::ES, 3, 2},
    128                                                      {Profile::ES, 3, 1},
    129                                                      {Profile::ES, 3, 0}}};
    130 
    131   std::span<const Version> versions_to_try = vlist;
    132   if (ShouldPreferESContext())
    133   {
    134     // move ES versions to the front
    135     Version* new_versions_to_try = static_cast<Version*>(alloca(sizeof(Version) * versions_to_try.size()));
    136     size_t count = 0;
    137     for (const Version& cv : versions_to_try)
    138     {
    139       if (cv.profile == Profile::ES)
    140         new_versions_to_try[count++] = cv;
    141     }
    142     for (const Version& cv : versions_to_try)
    143     {
    144       if (cv.profile != Profile::ES)
    145         new_versions_to_try[count++] = cv;
    146     }
    147     versions_to_try = std::span<const Version>(new_versions_to_try, versions_to_try.size());
    148   }
    149 
    150   std::unique_ptr<OpenGLContext> context;
    151 #if defined(_WIN32) && !defined(_M_ARM64)
    152   context = OpenGLContextWGL::Create(wi, versions_to_try, error);
    153 #elif defined(__APPLE__)
    154   context = OpenGLContextAGL::Create(wi, versions_to_try, error);
    155 #elif defined(__ANDROID__)
    156   context = ContextEGLAndroid::Create(wi, versions_to_try, error);
    157 #else
    158 #if defined(ENABLE_X11)
    159   if (wi.type == WindowInfo::Type::X11)
    160     context = OpenGLContextEGLX11::Create(wi, versions_to_try, error);
    161 #endif
    162 #if defined(ENABLE_WAYLAND)
    163   if (wi.type == WindowInfo::Type::Wayland)
    164     context = OpenGLContextEGLWayland::Create(wi, versions_to_try, error);
    165 #endif
    166   if (wi.type == WindowInfo::Type::Surfaceless)
    167     context = OpenGLContextEGL::Create(wi, versions_to_try, error);
    168 #endif
    169 
    170   if (!context)
    171     return nullptr;
    172 
    173   INFO_LOG(context->IsGLES() ? "Created an OpenGL ES context" : "Created an OpenGL context");
    174 
    175   // TODO: Not thread-safe.
    176   static OpenGLContext* context_being_created;
    177   context_being_created = context.get();
    178 
    179   // load up glad
    180   if (!context->IsGLES())
    181   {
    182     if (!gladLoadGL([](const char* name) { return (GLADapiproc)context_being_created->GetProcAddress(name); }))
    183     {
    184       Error::SetStringView(error, "Failed to load GL functions for GLAD");
    185       return nullptr;
    186     }
    187   }
    188   else
    189   {
    190     if (!gladLoadGLES2([](const char* name) { return (GLADapiproc)context_being_created->GetProcAddress(name); }))
    191     {
    192       Error::SetStringView(error, "Failed to load GLES functions for GLAD");
    193       return nullptr;
    194     }
    195   }
    196 
    197   const char* gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
    198   const char* gl_renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
    199   const char* gl_version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
    200   const char* gl_shading_language_version = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
    201   INFO_LOG("GL_VENDOR: {}", gl_vendor);
    202   INFO_LOG("GL_RENDERER: {}", gl_renderer);
    203   INFO_LOG("GL_VERSION: {}", gl_version);
    204   INFO_LOG("GL_SHADING_LANGUAGE_VERSION: {}", gl_shading_language_version);
    205 
    206   DisableBrokenExtensions(gl_vendor, gl_renderer, gl_version);
    207 
    208   return context;
    209 }