Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/drivers/egl/egl_manager.cpp
20952 views
1
/**************************************************************************/
2
/* egl_manager.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "egl_manager.h"
32
33
#include "core/crypto/crypto_core.h"
34
#include "core/io/dir_access.h"
35
#include "core/io/file_access.h"
36
#include "drivers/gles3/rasterizer_gles3.h"
37
38
#ifdef EGL_ENABLED
39
40
#if defined(EGL_STATIC)
41
42
#define GLAD_EGL_VERSION_1_5 true
43
44
#ifdef EGL_EXT_platform_base
45
#define GLAD_EGL_EXT_platform_base 1
46
#endif
47
48
#define KHRONOS_STATIC 1
49
extern "C" EGLAPI void EGLAPIENTRY eglSetBlobCacheFuncsANDROID(EGLDisplay dpy, EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get);
50
extern "C" EGLAPI EGLDisplay EGLAPIENTRY eglGetPlatformDisplayEXT(EGLenum platform, void *native_display, const EGLint *attrib_list);
51
#undef KHRONOS_STATIC
52
53
#endif // defined(EGL_STATIC)
54
55
#ifndef EGL_EXT_platform_base
56
#define GLAD_EGL_EXT_platform_base 0
57
#endif
58
59
#ifdef WINDOWS_ENABLED
60
// Unofficial ANGLE extension: EGL_ANGLE_surface_orientation
61
#ifndef EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE
62
#define EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE 0x33A7
63
#define EGL_SURFACE_ORIENTATION_ANGLE 0x33A8
64
#define EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE 0x0001
65
#define EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE 0x0002
66
#endif
67
#endif
68
69
// Creates and caches a GLDisplay. Returns -1 on error.
70
int EGLManager::_get_gldisplay_id(void *p_display) {
71
// Look for a cached GLDisplay.
72
for (unsigned int i = 0; i < displays.size(); i++) {
73
if (displays[i].display == p_display) {
74
return i;
75
}
76
}
77
78
// We didn't find any, so we'll have to create one, along with its own
79
// EGLDisplay and EGLContext.
80
GLDisplay new_gldisplay;
81
new_gldisplay.display = p_display;
82
83
if (GLAD_EGL_VERSION_1_5) {
84
Vector<EGLAttrib> attribs = _get_platform_display_attributes();
85
new_gldisplay.egl_display = eglGetPlatformDisplay(_get_platform_extension_enum(), new_gldisplay.display, (attribs.size() > 0) ? attribs.ptr() : nullptr);
86
} else if (GLAD_EGL_EXT_platform_base) {
87
#ifdef EGL_EXT_platform_base
88
// eglGetPlatformDisplayEXT wants its attributes as EGLint, so we'll truncate
89
// what we already have. It's a bit naughty but I'm really not sure what else
90
// we could do here.
91
Vector<EGLint> attribs;
92
for (const EGLAttrib &attrib : _get_platform_display_attributes()) {
93
attribs.push_back((EGLint)attrib);
94
}
95
96
new_gldisplay.egl_display = eglGetPlatformDisplayEXT(_get_platform_extension_enum(), new_gldisplay.display, (attribs.size() > 0) ? attribs.ptr() : nullptr);
97
#endif // EGL_EXT_platform_base
98
} else {
99
NativeDisplayType *native_display_type = (NativeDisplayType *)new_gldisplay.display;
100
new_gldisplay.egl_display = eglGetDisplay(*native_display_type);
101
}
102
103
ERR_FAIL_COND_V(eglGetError() != EGL_SUCCESS, -1);
104
105
ERR_FAIL_COND_V_MSG(new_gldisplay.egl_display == EGL_NO_DISPLAY, -1, "Can't create an EGL display.");
106
107
if (!eglInitialize(new_gldisplay.egl_display, nullptr, nullptr)) {
108
ERR_FAIL_V_MSG(-1, "Can't initialize an EGL display.");
109
}
110
111
if (!eglBindAPI(_get_platform_api_enum())) {
112
ERR_FAIL_V_MSG(-1, "OpenGL not supported.");
113
}
114
115
Error err = _gldisplay_create_context(new_gldisplay);
116
117
if (err != OK) {
118
eglTerminate(new_gldisplay.egl_display);
119
ERR_FAIL_V(-1);
120
}
121
122
#ifdef EGL_ANDROID_blob_cache
123
#if defined(EGL_STATIC)
124
bool has_blob_cache = true;
125
#else
126
bool has_blob_cache = (eglSetBlobCacheFuncsANDROID != nullptr);
127
#endif
128
if (has_blob_cache && !shader_cache_dir.is_empty()) {
129
eglSetBlobCacheFuncsANDROID(new_gldisplay.egl_display, &EGLManager::_set_cache, &EGLManager::_get_cache);
130
}
131
#endif
132
133
#ifdef WINDOWS_ENABLED
134
String client_extensions_string = eglQueryString(new_gldisplay.egl_display, EGL_EXTENSIONS);
135
if (eglGetError() == EGL_SUCCESS) {
136
Vector<String> egl_extensions = client_extensions_string.split(" ");
137
138
if (egl_extensions.has("EGL_ANGLE_surface_orientation")) {
139
new_gldisplay.has_EGL_ANGLE_surface_orientation = true;
140
print_verbose("EGL: EGL_ANGLE_surface_orientation is supported.");
141
}
142
}
143
#endif
144
145
displays.push_back(new_gldisplay);
146
147
// Return the new GLDisplay's ID.
148
return displays.size() - 1;
149
}
150
151
#ifdef EGL_ANDROID_blob_cache
152
String EGLManager::shader_cache_dir;
153
154
void EGLManager::_set_cache(const void *p_key, EGLsizeiANDROID p_key_size, const void *p_value, EGLsizeiANDROID p_value_size) {
155
String name = CryptoCore::b64_encode_str((const uint8_t *)p_key, p_key_size).replace_char('/', '_');
156
String path = shader_cache_dir.path_join(name) + ".cache";
157
158
Error err = OK;
159
Ref<FileAccess> file = FileAccess::open(path, FileAccess::WRITE, &err);
160
if (err != OK) {
161
return;
162
}
163
file->store_buffer((const uint8_t *)p_value, p_value_size);
164
}
165
166
EGLsizeiANDROID EGLManager::_get_cache(const void *p_key, EGLsizeiANDROID p_key_size, void *p_value, EGLsizeiANDROID p_value_size) {
167
String name = CryptoCore::b64_encode_str((const uint8_t *)p_key, p_key_size).replace_char('/', '_');
168
String path = shader_cache_dir.path_join(name) + ".cache";
169
170
Error err = OK;
171
Ref<FileAccess> file = FileAccess::open(path, FileAccess::READ, &err);
172
if (err != OK) {
173
return 0;
174
}
175
EGLsizeiANDROID len = file->get_length();
176
if (len <= p_value_size) {
177
file->get_buffer((uint8_t *)p_value, len);
178
}
179
return len;
180
}
181
#endif
182
183
Error EGLManager::_gldisplay_create_context(GLDisplay &p_gldisplay) {
184
EGLint attribs[] = {
185
EGL_RED_SIZE,
186
1,
187
EGL_BLUE_SIZE,
188
1,
189
EGL_GREEN_SIZE,
190
1,
191
EGL_DEPTH_SIZE,
192
24,
193
EGL_NONE,
194
};
195
196
EGLint attribs_layered[] = {
197
EGL_RED_SIZE,
198
8,
199
EGL_GREEN_SIZE,
200
8,
201
EGL_GREEN_SIZE,
202
8,
203
EGL_ALPHA_SIZE,
204
8,
205
EGL_DEPTH_SIZE,
206
24,
207
EGL_NONE,
208
};
209
210
EGLint config_count = 0;
211
212
if (OS::get_singleton()->is_layered_allowed()) {
213
eglChooseConfig(p_gldisplay.egl_display, attribs_layered, &p_gldisplay.egl_config, 1, &config_count);
214
} else {
215
eglChooseConfig(p_gldisplay.egl_display, attribs, &p_gldisplay.egl_config, 1, &config_count);
216
}
217
218
ERR_FAIL_COND_V(eglGetError() != EGL_SUCCESS, ERR_BUG);
219
ERR_FAIL_COND_V(config_count == 0, ERR_UNCONFIGURED);
220
221
Vector<EGLint> context_attribs = _get_platform_context_attribs();
222
p_gldisplay.egl_context = eglCreateContext(p_gldisplay.egl_display, p_gldisplay.egl_config, EGL_NO_CONTEXT, (context_attribs.size() > 0) ? context_attribs.ptr() : nullptr);
223
ERR_FAIL_COND_V_MSG(p_gldisplay.egl_context == EGL_NO_CONTEXT, ERR_CANT_CREATE, vformat("Can't create an EGL context. Error code: %d", eglGetError()));
224
225
return OK;
226
}
227
228
Error EGLManager::open_display(void *p_display) {
229
int gldisplay_id = _get_gldisplay_id(p_display);
230
if (gldisplay_id < 0) {
231
return ERR_CANT_CREATE;
232
} else {
233
return OK;
234
}
235
}
236
237
int EGLManager::display_get_native_visual_id(void *p_display) {
238
int gldisplay_id = _get_gldisplay_id(p_display);
239
ERR_FAIL_COND_V(gldisplay_id < 0, ERR_CANT_CREATE);
240
241
GLDisplay gldisplay = displays[gldisplay_id];
242
243
EGLint native_visual_id = -1;
244
245
if (!eglGetConfigAttrib(gldisplay.egl_display, gldisplay.egl_config, EGL_NATIVE_VISUAL_ID, &native_visual_id)) {
246
ERR_FAIL_V(-1);
247
}
248
249
return native_visual_id;
250
}
251
252
Error EGLManager::window_create(DisplayServer::WindowID p_window_id, void *p_display, void *p_native_window, int p_width, int p_height) {
253
int gldisplay_id = _get_gldisplay_id(p_display);
254
ERR_FAIL_COND_V(gldisplay_id < 0, ERR_CANT_CREATE);
255
256
GLDisplay &gldisplay = displays[gldisplay_id];
257
258
// In order to ensure a fast lookup, make sure we got enough elements in the
259
// windows local vector to use the window id as an index.
260
if (p_window_id >= (int)windows.size()) {
261
windows.resize(p_window_id + 1);
262
}
263
264
GLWindow &glwindow = windows[p_window_id];
265
glwindow.gldisplay_id = gldisplay_id;
266
267
Vector<EGLAttrib> egl_attribs;
268
269
#ifdef WINDOWS_ENABLED
270
if (gldisplay.has_EGL_ANGLE_surface_orientation) {
271
EGLint optimal_orientation;
272
if (eglGetConfigAttrib(gldisplay.egl_display, gldisplay.egl_config, EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE, &optimal_orientation)) {
273
// We only need to support inverting Y for optimizing ANGLE on D3D11.
274
if (optimal_orientation & EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE && !(optimal_orientation & EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE)) {
275
egl_attribs.push_back(EGL_SURFACE_ORIENTATION_ANGLE);
276
egl_attribs.push_back(EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE);
277
}
278
} else {
279
ERR_PRINT(vformat("Failed to get EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE, error: 0x%08X", eglGetError()));
280
}
281
}
282
283
if (!egl_attribs.is_empty()) {
284
egl_attribs.push_back(EGL_NONE);
285
}
286
#endif
287
288
if (GLAD_EGL_VERSION_1_5) {
289
glwindow.egl_surface = eglCreatePlatformWindowSurface(gldisplay.egl_display, gldisplay.egl_config, p_native_window, egl_attribs.ptr());
290
} else {
291
EGLNativeWindowType *native_window_type = (EGLNativeWindowType *)p_native_window;
292
glwindow.egl_surface = eglCreateWindowSurface(gldisplay.egl_display, gldisplay.egl_config, *native_window_type, nullptr);
293
}
294
295
if (glwindow.egl_surface == EGL_NO_SURFACE) {
296
return ERR_CANT_CREATE;
297
}
298
299
glwindow.initialized = true;
300
301
#ifdef WINDOWS_ENABLED
302
if (gldisplay.has_EGL_ANGLE_surface_orientation) {
303
EGLint orientation;
304
if (eglQuerySurface(gldisplay.egl_display, glwindow.egl_surface, EGL_SURFACE_ORIENTATION_ANGLE, &orientation)) {
305
if (orientation & EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE && !(orientation & EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE)) {
306
glwindow.flipped_y = true;
307
print_verbose("EGL: Using optimal surface orientation: Invert Y");
308
}
309
} else {
310
ERR_PRINT(vformat("Failed to get EGL_SURFACE_ORIENTATION_ANGLE, error: 0x%08X", eglGetError()));
311
}
312
}
313
#endif
314
315
window_make_current(p_window_id);
316
317
return OK;
318
}
319
320
void EGLManager::window_destroy(DisplayServer::WindowID p_window_id) {
321
ERR_FAIL_INDEX(p_window_id, (int)windows.size());
322
323
GLWindow &glwindow = windows[p_window_id];
324
325
if (!glwindow.initialized) {
326
return;
327
}
328
329
glwindow.initialized = false;
330
331
ERR_FAIL_INDEX(glwindow.gldisplay_id, (int)displays.size());
332
GLDisplay &gldisplay = displays[glwindow.gldisplay_id];
333
334
if (glwindow.egl_surface != EGL_NO_SURFACE) {
335
eglDestroySurface(gldisplay.egl_display, glwindow.egl_surface);
336
glwindow.egl_surface = nullptr;
337
}
338
}
339
340
void EGLManager::release_current() {
341
if (!current_window) {
342
return;
343
}
344
345
GLDisplay &current_display = displays[current_window->gldisplay_id];
346
347
eglMakeCurrent(current_display.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
348
}
349
350
void EGLManager::swap_buffers() {
351
if (!current_window) {
352
return;
353
}
354
355
if (!current_window->initialized) {
356
WARN_PRINT("Current OpenGL window is uninitialized!");
357
return;
358
}
359
360
GLDisplay &current_display = displays[current_window->gldisplay_id];
361
362
eglSwapBuffers(current_display.egl_display, current_window->egl_surface);
363
}
364
365
void EGLManager::window_make_current(DisplayServer::WindowID p_window_id) {
366
if (p_window_id == DisplayServer::INVALID_WINDOW_ID) {
367
return;
368
}
369
370
GLWindow &glwindow = windows[p_window_id];
371
372
if (&glwindow == current_window || !glwindow.initialized) {
373
return;
374
}
375
376
current_window = &glwindow;
377
378
GLDisplay &current_display = displays[current_window->gldisplay_id];
379
380
eglMakeCurrent(current_display.egl_display, current_window->egl_surface, current_window->egl_surface, current_display.egl_context);
381
382
#ifdef WINDOWS_ENABLED
383
RasterizerGLES3::set_screen_flipped_y(glwindow.flipped_y);
384
#endif
385
}
386
387
void EGLManager::set_use_vsync(bool p_use) {
388
// We need an active window to get a display to set the vsync.
389
if (!current_window) {
390
return;
391
}
392
393
GLDisplay &disp = displays[current_window->gldisplay_id];
394
395
int swap_interval = p_use ? 1 : 0;
396
397
if (!eglSwapInterval(disp.egl_display, swap_interval)) {
398
WARN_PRINT("Could not set V-Sync mode.");
399
}
400
401
use_vsync = p_use;
402
}
403
404
bool EGLManager::is_using_vsync() const {
405
return use_vsync;
406
}
407
408
EGLContext EGLManager::get_context(DisplayServer::WindowID p_window_id) {
409
GLWindow &glwindow = windows[p_window_id];
410
411
if (!glwindow.initialized) {
412
return EGL_NO_CONTEXT;
413
}
414
415
GLDisplay &display = displays[glwindow.gldisplay_id];
416
417
return display.egl_context;
418
}
419
420
EGLDisplay EGLManager::get_display(DisplayServer::WindowID p_window_id) {
421
GLWindow &glwindow = windows[p_window_id];
422
423
if (!glwindow.initialized) {
424
return EGL_NO_CONTEXT;
425
}
426
427
GLDisplay &display = displays[glwindow.gldisplay_id];
428
429
return display.egl_display;
430
}
431
432
EGLConfig EGLManager::get_config(DisplayServer::WindowID p_window_id) {
433
GLWindow &glwindow = windows[p_window_id];
434
435
if (!glwindow.initialized) {
436
return nullptr;
437
}
438
439
GLDisplay &display = displays[glwindow.gldisplay_id];
440
441
return display.egl_config;
442
}
443
444
Error EGLManager::initialize(void *p_native_display) {
445
#if defined(GLAD_ENABLED) && !defined(EGL_STATIC)
446
// Loading EGL with a new display gets us just the bare minimum API. We'll then
447
// have to temporarily get a proper display and reload EGL once again to
448
// initialize everything else.
449
if (!gladLoaderLoadEGL(EGL_NO_DISPLAY)) {
450
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Can't load EGL dynamic library.");
451
}
452
453
EGLDisplay tmp_display = EGL_NO_DISPLAY;
454
455
if (GLAD_EGL_EXT_platform_base) {
456
#ifdef EGL_EXT_platform_base
457
// eglGetPlatformDisplayEXT wants its attributes as EGLint.
458
Vector<EGLint> attribs;
459
for (const EGLAttrib &attrib : _get_platform_display_attributes()) {
460
attribs.push_back((EGLint)attrib);
461
}
462
tmp_display = eglGetPlatformDisplayEXT(_get_platform_extension_enum(), p_native_display, attribs.ptr());
463
#endif // EGL_EXT_platform_base
464
} else {
465
WARN_PRINT("EGL: EGL_EXT_platform_base not found during init, using default platform.");
466
EGLNativeDisplayType *native_display_type = (EGLNativeDisplayType *)p_native_display;
467
tmp_display = eglGetDisplay(*native_display_type);
468
}
469
470
if (tmp_display == EGL_NO_DISPLAY) {
471
eglTerminate(tmp_display);
472
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Can't get a valid initial EGL display.");
473
}
474
475
eglInitialize(tmp_display, nullptr, nullptr);
476
477
int version = gladLoaderLoadEGL(tmp_display);
478
if (!version) {
479
eglTerminate(tmp_display);
480
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Can't load EGL dynamic library.");
481
}
482
483
int major = GLAD_VERSION_MAJOR(version);
484
int minor = GLAD_VERSION_MINOR(version);
485
486
print_verbose(vformat("Loaded EGL %d.%d", major, minor));
487
488
ERR_FAIL_COND_V_MSG(!GLAD_EGL_VERSION_1_4, ERR_UNAVAILABLE, vformat("EGL version is too old! %d.%d < 1.4", major, minor));
489
490
eglTerminate(tmp_display);
491
#endif
492
493
#ifdef EGL_ANDROID_blob_cache
494
shader_cache_dir = Engine::get_singleton()->get_shader_cache_path();
495
if (shader_cache_dir.is_empty()) {
496
shader_cache_dir = "user://";
497
}
498
Error err = OK;
499
Ref<DirAccess> da = DirAccess::open(shader_cache_dir);
500
if (da.is_null()) {
501
ERR_PRINT("EGL: Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
502
shader_cache_dir = String();
503
} else {
504
err = da->change_dir(String("shader_cache").path_join("EGL"));
505
if (err != OK) {
506
err = da->make_dir_recursive(String("shader_cache").path_join("EGL"));
507
}
508
if (err != OK) {
509
ERR_PRINT("EGL: Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
510
shader_cache_dir = String();
511
} else {
512
shader_cache_dir = shader_cache_dir.path_join(String("shader_cache").path_join("EGL"));
513
}
514
}
515
#endif
516
517
String client_extensions_string = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
518
519
// If the above method fails, we don't support client extensions, so there's nothing to check.
520
if (eglGetError() == EGL_SUCCESS) {
521
const char *platform = _get_platform_extension_name();
522
if (!client_extensions_string.split(" ").has(platform)) {
523
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, vformat("EGL platform extension \"%s\" not found.", platform));
524
}
525
}
526
527
return OK;
528
}
529
530
EGLManager::EGLManager() {
531
}
532
533
EGLManager::~EGLManager() {
534
for (unsigned int i = 0; i < displays.size(); i++) {
535
eglTerminate(displays[i].egl_display);
536
}
537
}
538
539
#endif // EGL_ENABLED
540
541