1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
|
#define AL_LOG_SECTION "egl"
#include <al/log.h>
#include <al/macros.h>
#ifdef STELA_API_OPENGL
#include "egl_common.h"
#define STELA_GL_API EGL_OPENGL_API
#define STELA_GLES_API EGL_OPENGL_ES_API
#define STELA_GL_BIT EGL_OPENGL_BIT
#define STELA_GLES2_BIT EGL_OPENGL_ES2_BIT
#define STELA_GLES3_BIT EGL_OPENGL_ES3_BIT
#define STELA_GL_CORE_PROFILE_BIT EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT
#include "gl_versions.h"
PASTE_GL_VERSIONS()
#endif
bool stl_maybe_load_egl(void)
{
#ifdef STELA_USE_GLAD
s32 egl_version = gladLoaderLoadEGL(NULL);
if (!egl_version) {
log_error("Failed to pre-load EGL (glad).");
return false;
}
#endif
return true;
}
bool stl_load_egl(struct stl_egl_global *global)
{
EGLint major, minor;
if (!eglInitialize(global->display, &major, &minor)) {
log_error("Failed to initialize.");
return false;
}
log_debug("eglInitialize()'d version %d.%d.", major, minor);
#ifdef STELA_USE_GLAD
s32 egl_version = gladLoaderLoadEGL(global->display);
if (!egl_version) {
log_error("Failed to load EGL (glad).");
return false;
}
log_debug("Using EGL (glad) version %s.", eglQueryString(global->display, EGL_VERSION));
#else
log_debug("Using EGL version %s.", eglQueryString(global->display, EGL_VERSION));
#endif
return true;
}
void stl_terminate_egl(struct stl_egl_global *global)
{
if (global->display != EGL_NO_DISPLAY) {
if (!eglTerminate(global->display)) {
log_warn("Failed to eglTerminate().");
}
}
}
// https://gitlab.freedesktop.org/mesa/kmscube/-/blob/master/common.c#L162
static EGLint match_config_to_visual(EGLDisplay display, EGLint visual_id, EGLConfig *configs, EGLint count)
{
EGLint id;
for (EGLint i = 0; i < count; i++) {
if (!eglGetConfigAttrib(display, configs[i], EGL_NATIVE_VISUAL_ID, &id)) {
continue;
}
if (id == visual_id) return i;
}
return -1;
}
static bool egl_choose_config(EGLDisplay display, const EGLint *attribs, EGLint visual_id, EGLConfig *config)
{
EGLint count = 0;
if (!eglGetConfigs(display, NULL, 0, &count) || count <= 0) {
log_error("No configs to choose from.");
return false;
}
EGLint matched = 0;
EGLConfig *configs = (EGLConfig *)al_malloc(count * sizeof(EGLConfig));
if (!eglChooseConfig(display, attribs, configs, count, &matched) || !matched) {
log_error("No configs with appropriate attributes.");
al_free(configs);
return false;
}
EGLint index = visual_id ? match_config_to_visual(display, visual_id, config, matched) : 0;
if (index != -1) *config = configs[index];
al_free(configs);
return index != -1;
}
bool stl_init_egl_context(struct stl_egl *egl, struct stl_egl_global *global, EGLint visual_id, EGLConfig *config)
{
egl->display = global->display;
egl->context = EGL_NO_CONTEXT;
for (u32 i = 0; i < ARRAY_SIZE(stl_gl_vers); i++) {
const EGLint context_attr_gles[] = {
EGL_CONTEXT_CLIENT_VERSION, stl_gl_vers[i].major,
EGL_NONE
};
const EGLint context_attr_gl[] = {
#ifdef AL_DEBUG
EGL_CONTEXT_OPENGL_DEBUG, EGL_TRUE,
#endif
EGL_CONTEXT_MAJOR_VERSION, stl_gl_vers[i].major,
EGL_CONTEXT_MINOR_VERSION, stl_gl_vers[i].minor,
EGL_CONTEXT_OPENGL_PROFILE_MASK, stl_gl_vers[i].profile,
EGL_NONE
};
STELA_LOG_GL_CONTEXT_CREATION(stl_gl_vers[i]);
const EGLint *context_attr = stl_gl_vers[i].api == STELA_GL_API ?
context_attr_gl : context_attr_gles;
eglBindAPI(stl_gl_vers[i].api);
#define STELA_INCLUDE_DEPTH_BUFFER
const EGLint window_attr[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
#ifdef STELA_INCLUDE_DEPTH_BUFFER
EGL_DEPTH_SIZE, 24,
EGL_STENCIL_SIZE, 8,
#else
EGL_DEPTH_SIZE, 0,
EGL_STENCIL_SIZE, 0,
#endif
EGL_RENDERABLE_TYPE, stl_gl_vers[i].type,
EGL_SAMPLES, 0,
EGL_NONE
};
if (!egl_choose_config(egl->display, window_attr, visual_id, config)) {
log_error("Failed to choose a display config.");
continue;
}
egl->context = eglCreateContext(egl->display, *config, EGL_NO_CONTEXT, context_attr);
if (egl->context == EGL_NO_CONTEXT) {
continue;
} else {
break;
}
}
if (egl->context == EGL_NO_CONTEXT) {
log_error("Failed to create context.");
return false;
}
egl->current = EGL_NO_CONTEXT;
egl->surface = EGL_NO_SURFACE;
return true;
}
bool stl_egl_make_current(struct stl_egl *egl)
{
if (egl->context != egl->current) {
al_assert(egl->current == EGL_NO_CONTEXT);
egl->current = egl->context;
return eglMakeCurrent(egl->display, egl->surface, egl->surface, egl->context);
}
return true;
}
void stl_egl_release_current(struct stl_egl *egl)
{
// https://gitlab.freedesktop.org/mesa/mesa/-/issues/6547
// libplacebo calls release_current() after swapping buffers. That locks in the
// current backbuffer size. So, if all that happens for the next frame is:
// 1. wl_egl_window_resize()
// 2. resize libplacebo swapchain
// 3. start frame
// 4. submit frame
// 5. swap buffers
// The frame that is presented will still have the dimensions of the previous backbuffer.
// We only support 1 context at a time, so this shouldn't be an issue.
al_assert(egl->context == egl->current);
#if 0
if (egl->current != EGL_NO_CONTEXT) {
eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
egl->current = EGL_NO_CONTEXT;
}
#endif
}
void stl_egl_swap_interval(struct stl_egl *egl, EGLint interval)
{
eglSwapInterval(egl->display, interval);
}
void stl_egl_swap_buffers(struct stl_egl *egl)
{
eglSwapBuffers(egl->display, egl->surface);
}
bool stl_egl_is_ready(struct stl_egl *egl)
{
return egl->context != EGL_NO_CONTEXT;
}
void stl_egl_destroy_surface(struct stl_egl *egl)
{
eglDestroySurface(egl->display, egl->surface);
}
void stl_egl_destroy_context(struct stl_egl *egl)
{
eglDestroyContext(egl->display, egl->context);
}
#define ERROR_CASE_STR(value) case value: return #value;
static const char *eglGetErrorString(EGLint error)
{
switch (error) {
ERROR_CASE_STR(EGL_SUCCESS)
ERROR_CASE_STR(EGL_NOT_INITIALIZED)
ERROR_CASE_STR(EGL_BAD_ACCESS)
ERROR_CASE_STR(EGL_BAD_ALLOC)
ERROR_CASE_STR(EGL_BAD_ATTRIBUTE)
ERROR_CASE_STR(EGL_BAD_CONTEXT)
ERROR_CASE_STR(EGL_BAD_CONFIG)
ERROR_CASE_STR(EGL_BAD_CURRENT_SURFACE)
ERROR_CASE_STR(EGL_BAD_DISPLAY)
ERROR_CASE_STR(EGL_BAD_SURFACE)
ERROR_CASE_STR(EGL_BAD_MATCH)
ERROR_CASE_STR(EGL_BAD_PARAMETER)
ERROR_CASE_STR(EGL_BAD_NATIVE_PIXMAP)
ERROR_CASE_STR(EGL_BAD_NATIVE_WINDOW)
ERROR_CASE_STR(EGL_CONTEXT_LOST)
default: return "Unknown";
}
}
#undef ERROR_CASE_STR
void stl_egl_check_error()
{
EGLint error;
while ((error = eglGetError()) != EGL_SUCCESS) {
log_error("EGL error %s, %d.", eglGetErrorString(error), error);
}
}
|