ohmu
·
2026-10-15
1#include "opengl.h"
2#include "common/debug.h"
3#include "common/library.h"
4#include "common/io.h"
5
6#if __has_include(<GL/gl.h>)
7 #include <GL/gl.h>
8#elif __APPLE__
9 #define GL_SILENCE_DEPRECATION 1
10 #include <OpenGL/gl.h>
11#else
12 #define FF_HAVE_NO_GL 1
13#endif
14
15#ifndef FF_HAVE_NO_GL
16
17 #ifndef GL_SHADING_LANGUAGE_VERSION // For WGL
18 #define GL_SHADING_LANGUAGE_VERSION 0x8B8C
19 #endif
20
21void ffOpenGLHandleResult(FFOpenGLResult* result, typeof(&glGetString) ffglGetString) {
22 ffStrbufAppendS(&result->version, (const char*) ffglGetString(GL_VERSION));
23 ffStrbufAppendS(&result->renderer, (const char*) ffglGetString(GL_RENDERER));
24 ffStrbufAppendS(&result->vendor, (const char*) ffglGetString(GL_VENDOR));
25 ffStrbufAppendS(&result->slv, (const char*) ffglGetString(GL_SHADING_LANGUAGE_VERSION));
26}
27
28 #if defined(FF_HAVE_EGL) || __has_include(<EGL/egl.h>)
29 #include "common/io.h"
30
31 #define EGL_EGL_PROTOTYPES 1
32 #define EGL_EGLEXT_PROTOTYPES 1
33 #include <EGL/egl.h>
34 #include <EGL/eglext.h>
35
36typedef struct EGLData {
37 FF_LIBRARY_SYMBOL(glGetString)
38 FF_LIBRARY_SYMBOL(eglQueryString)
39 FF_LIBRARY_SYMBOL(eglInitialize)
40 FF_LIBRARY_SYMBOL(eglBindAPI)
41 FF_LIBRARY_SYMBOL(eglGetConfigs)
42 FF_LIBRARY_SYMBOL(eglCreatePbufferSurface)
43 FF_LIBRARY_SYMBOL(eglCreateContext)
44 FF_LIBRARY_SYMBOL(eglMakeCurrent)
45 FF_LIBRARY_SYMBOL(eglDestroyContext)
46 FF_LIBRARY_SYMBOL(eglDestroySurface)
47 FF_LIBRARY_SYMBOL(eglTerminate)
48
49 EGLDisplay display;
50 EGLConfig config;
51 EGLSurface surface;
52 EGLContext context;
53} EGLData;
54
55static const char* eglHandleContext(FFOpenGLResult* result, EGLData* data) {
56 FF_DEBUG("Making EGL context current");
57 if (data->ffeglMakeCurrent(data->display, data->surface, data->surface, data->context) != EGL_TRUE) {
58 FF_DEBUG("eglMakeCurrent() returned EGL_FALSE");
59 return "eglMakeCurrent returned EGL_FALSE";
60 }
61
62 ffOpenGLHandleResult(result, data->ffglGetString);
63 ffStrbufSetF(&result->library, "EGL %s", data->ffeglQueryString(data->display, EGL_VERSION));
64 FF_DEBUG("OpenGL via EGL detected: version='%s', renderer='%s', vendor='%s', slv='%s', library='%s'",
65 result->version.chars,
66 result->renderer.chars,
67 result->vendor.chars,
68 result->slv.chars,
69 result->library.chars);
70 return nullptr;
71}
72
73static const char* eglHandleSurface(FFOpenGLResult* result, EGLData* data, bool gles) {
74 FF_DEBUG("Creating EGL context (preferred API=%s, client version=%d)", gles ? "OpenGL ES" : "OpenGL", gles ? 2 : 1);
75 data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]) { EGL_CONTEXT_CLIENT_VERSION, gles ? 2 : 1, // Try GLES 2.0+ first
76 EGL_NONE });
77 if (data->context == EGL_NO_CONTEXT && gles) // Some ANGLE builds support GLES 1.1 only
78 {
79 FF_DEBUG("EGL context creation with GLES 2.x failed, retrying with default attributes (GLES 1.1 fallback)");
80 data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]) { EGL_NONE });
81 }
82 if (data->context == EGL_NO_CONTEXT) {
83 FF_DEBUG("eglCreateContext() returned EGL_NO_CONTEXT");
84 return "eglCreateContext returned EGL_NO_CONTEXT";
85 }
86
87 FF_DEBUG("EGL context created successfully");
88
89 const char* error = eglHandleContext(result, data);
90 FF_DEBUG("eglHandleContext() returns: %s", error ?: "success");
91
92 FF_DEBUG("Releasing current EGL context");
93 data->ffeglMakeCurrent(data->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
94
95 FF_DEBUG("Destroying EGL context");
96 data->ffeglDestroyContext(data->display, data->context);
97 return error;
98}
99
100static const char* eglHandleDisplay(FFOpenGLResult* result, EGLData* data) {
101 // try use OpenGL API. If failed, use the default API (usually OpenGL ES)
102 bool gles = !data->ffeglBindAPI(EGL_OPENGL_API);
103 FF_DEBUG("eglBindAPI(EGL_OPENGL_API) %s, effective API=%s",
104 gles ? "failed" : "succeeded",
105 gles ? "default (usually OpenGL ES)" : "OpenGL");
106
107 EGLint eglConfigCount;
108 data->ffeglGetConfigs(data->display, &data->config, 1, &eglConfigCount);
109 FF_DEBUG("eglGetConfigs() returned %d config(s)", eglConfigCount);
110
111 if (eglConfigCount == 0) {
112 FF_DEBUG("No EGL config is available");
113 return "eglGetConfigs returned 0 configs";
114 }
115
116 FF_DEBUG("Creating EGL pbuffer surface (%dx%d)", FF_OPENGL_BUFFER_WIDTH, FF_OPENGL_BUFFER_HEIGHT);
117 data->surface = data->ffeglCreatePbufferSurface(data->display, data->config, (EGLint[]) { EGL_WIDTH, FF_OPENGL_BUFFER_WIDTH, EGL_HEIGHT, FF_OPENGL_BUFFER_HEIGHT, EGL_NONE });
118
119 if (data->surface == EGL_NO_SURFACE) {
120 FF_DEBUG("eglCreatePbufferSurface() returned EGL_NO_SURFACE");
121 return "eglCreatePbufferSurface returned EGL_NO_SURFACE";
122 }
123
124 FF_DEBUG("EGL pbuffer surface created successfully");
125
126 const char* error = eglHandleSurface(result, data, gles);
127 FF_DEBUG("eglHandleSurface() returns: %s", error ?: "success");
128
129 FF_DEBUG("Destroying EGL surface");
130 data->ffeglDestroySurface(data->display, data->surface);
131 return error;
132}
133
134static const char* eglHandlePreDisplay(FFOpenGLResult* result, EGLData* data) {
135 EGLint major, minor;
136 if (data->ffeglInitialize(data->display, &major, &minor) == EGL_FALSE) {
137 FF_DEBUG("eglInitialize() returned EGL_FALSE");
138 return "eglInitialize returned EGL_FALSE";
139 }
140
141 FF_DEBUG("EGL initialized successfully: %d.%d", major, minor);
142
143 const char* error = eglHandleDisplay(result, data);
144 FF_DEBUG("eglHandleDisplay() returns: %s", error ?: "success");
145
146 FF_DEBUG("Terminating EGL display connection");
147 data->ffeglTerminate(data->display);
148 return error;
149}
150
151const char* ffOpenGLDetectByEGL(FFOpenGLResult* result) {
152 FF_DEBUG("Starting OpenGL detection via EGL");
153 EGLData eglData;
154
155 FF_LIBRARY_LOAD_MESSAGE(egl, "libEGL" FF_LIBRARY_EXTENSION, 1);
156 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(egl, eglGetProcAddress);
157 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglQueryString);
158 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglInitialize);
159 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglBindAPI);
160 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglGetConfigs);
161 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglCreatePbufferSurface);
162 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglCreateContext);
163 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglMakeCurrent);
164 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglDestroyContext);
165 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglDestroySurface);
166 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglTerminate);
167
168 FF_DEBUG("Resolving glGetString via eglGetProcAddress()");
169 eglData.ffglGetString = (typeof(&glGetString)) ffeglGetProcAddress("glGetString");
170 if (!eglData.ffglGetString) {
171 FF_DEBUG("eglGetProcAddress('glGetString') returned nullptr");
172 return "eglGetProcAddress(glGetString) returned nullptr";
173 }
174
175 FF_DEBUG("Loaded EGL library and required symbols");
176
177 FF_SUPPRESS_IO();
178
179 EGLDisplay display = EGL_NO_DISPLAY;
180 #if EGL_VERSION_1_5
181 PFNEGLGETPLATFORMDISPLAYEXTPROC ffeglGetPlatformDisplay = (PFNEGLGETPLATFORMDISPLAYEXTPROC) ffeglGetProcAddress("eglGetPlatformDisplay");
182 if (!ffeglGetPlatformDisplay) {
183 ffeglGetPlatformDisplay = (PFNEGLGETPLATFORMDISPLAYEXTPROC) ffeglGetProcAddress("eglGetPlatformDisplayEXT");
184 }
185 if (ffeglGetPlatformDisplay) {
186 FF_DEBUG("Trying eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA)");
187 display = ffeglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, nullptr /*EGL_DEFAULT_DISPLAY*/, nullptr);
188 FF_DEBUG("eglGetPlatformDisplay() %s", display == EGL_NO_DISPLAY ? "failed" : "succeeded");
189 } else {
190 FF_DEBUG("eglGetPlatformDisplay is unavailable, falling back to eglGetDisplay");
191 }
192
193 if (display == EGL_NO_DISPLAY)
194 #endif
195
196 {
197 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(egl, eglGetDisplay);
198
199 FF_DEBUG("Trying eglGetDisplay(EGL_DEFAULT_DISPLAY)");
200 display = ffeglGetDisplay(EGL_DEFAULT_DISPLAY);
201 if (display == EGL_NO_DISPLAY) {
202 FF_DEBUG("eglGetDisplay() returned EGL_NO_DISPLAY");
203 return "eglGetDisplay returned EGL_NO_DISPLAY";
204 }
205
206 FF_DEBUG("eglGetDisplay() succeeded");
207 }
208 eglData.display = display;
209
210 const char* error = eglHandlePreDisplay(result, &eglData);
211
212 FF_DEBUG("OpenGL detection via EGL returns: %s", error ?: "success");
213
214 return error;
215}
216
217 #endif // FF_HAVE_EGL
218
219#endif // FF_HAVE_NO_GL