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 }