egl_manager.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  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. #include "egl_manager.h"
  31. #ifdef EGL_ENABLED
  32. #if defined(EGL_STATIC)
  33. #define KHRONOS_STATIC 1
  34. #define GLAD_EGL_VERSION_1_5 true
  35. extern "C" EGLAPI void EGLAPIENTRY eglSetBlobCacheFuncsANDROID(EGLDisplay dpy, EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get);
  36. #undef KHRONOS_STATIC
  37. #endif
  38. // Creates and caches a GLDisplay. Returns -1 on error.
  39. int EGLManager::_get_gldisplay_id(void *p_display) {
  40. // Look for a cached GLDisplay.
  41. for (unsigned int i = 0; i < displays.size(); i++) {
  42. if (displays[i].display == p_display) {
  43. return i;
  44. }
  45. }
  46. // We didn't find any, so we'll have to create one, along with its own
  47. // EGLDisplay and EGLContext.
  48. GLDisplay new_gldisplay;
  49. new_gldisplay.display = p_display;
  50. if (GLAD_EGL_VERSION_1_5) {
  51. Vector<EGLAttrib> attribs = _get_platform_display_attributes();
  52. new_gldisplay.egl_display = eglGetPlatformDisplay(_get_platform_extension_enum(), new_gldisplay.display, (attribs.size() > 0) ? attribs.ptr() : nullptr);
  53. } else {
  54. NativeDisplayType *native_display_type = (NativeDisplayType *)new_gldisplay.display;
  55. new_gldisplay.egl_display = eglGetDisplay(*native_display_type);
  56. }
  57. ERR_FAIL_COND_V(eglGetError() != EGL_SUCCESS, -1);
  58. ERR_FAIL_COND_V_MSG(new_gldisplay.egl_display == EGL_NO_DISPLAY, -1, "Can't create an EGL display.");
  59. if (!eglInitialize(new_gldisplay.egl_display, nullptr, nullptr)) {
  60. ERR_FAIL_V_MSG(-1, "Can't initialize an EGL display.");
  61. }
  62. if (!eglBindAPI(_get_platform_api_enum())) {
  63. ERR_FAIL_V_MSG(-1, "OpenGL not supported.");
  64. }
  65. Error err = _gldisplay_create_context(new_gldisplay);
  66. if (err != OK) {
  67. eglTerminate(new_gldisplay.egl_display);
  68. ERR_FAIL_V(-1);
  69. }
  70. #ifdef EGL_ANDROID_blob_cache
  71. #if defined(EGL_STATIC)
  72. bool has_blob_cache = true;
  73. #else
  74. bool has_blob_cache = (eglSetBlobCacheFuncsANDROID != nullptr);
  75. #endif
  76. if (has_blob_cache && !shader_cache_dir.is_empty()) {
  77. eglSetBlobCacheFuncsANDROID(new_gldisplay.egl_display, &EGLManager::_set_cache, &EGLManager::_get_cache);
  78. }
  79. #endif
  80. displays.push_back(new_gldisplay);
  81. // Return the new GLDisplay's ID.
  82. return displays.size() - 1;
  83. }
  84. #ifdef EGL_ANDROID_blob_cache
  85. String EGLManager::shader_cache_dir;
  86. void EGLManager::_set_cache(const void *p_key, EGLsizeiANDROID p_key_size, const void *p_value, EGLsizeiANDROID p_value_size) {
  87. String name = CryptoCore::b64_encode_str((const uint8_t *)p_key, p_key_size).replace("/", "_");
  88. String path = shader_cache_dir.path_join(name) + ".cache";
  89. Error err = OK;
  90. Ref<FileAccess> file = FileAccess::open(path, FileAccess::WRITE, &err);
  91. if (err != OK) {
  92. return;
  93. }
  94. file->store_buffer((const uint8_t *)p_value, p_value_size);
  95. }
  96. EGLsizeiANDROID EGLManager::_get_cache(const void *p_key, EGLsizeiANDROID p_key_size, void *p_value, EGLsizeiANDROID p_value_size) {
  97. String name = CryptoCore::b64_encode_str((const uint8_t *)p_key, p_key_size).replace("/", "_");
  98. String path = shader_cache_dir.path_join(name) + ".cache";
  99. Error err = OK;
  100. Ref<FileAccess> file = FileAccess::open(path, FileAccess::READ, &err);
  101. if (err != OK) {
  102. return 0;
  103. }
  104. EGLsizeiANDROID len = file->get_length();
  105. if (len <= p_value_size) {
  106. file->get_buffer((uint8_t *)p_value, len);
  107. }
  108. return len;
  109. }
  110. #endif
  111. Error EGLManager::_gldisplay_create_context(GLDisplay &p_gldisplay) {
  112. EGLint attribs[] = {
  113. EGL_RED_SIZE,
  114. 1,
  115. EGL_BLUE_SIZE,
  116. 1,
  117. EGL_GREEN_SIZE,
  118. 1,
  119. EGL_DEPTH_SIZE,
  120. 24,
  121. EGL_NONE,
  122. };
  123. EGLint attribs_layered[] = {
  124. EGL_RED_SIZE,
  125. 8,
  126. EGL_GREEN_SIZE,
  127. 8,
  128. EGL_GREEN_SIZE,
  129. 8,
  130. EGL_ALPHA_SIZE,
  131. 8,
  132. EGL_DEPTH_SIZE,
  133. 24,
  134. EGL_NONE,
  135. };
  136. EGLint config_count = 0;
  137. if (OS::get_singleton()->is_layered_allowed()) {
  138. eglChooseConfig(p_gldisplay.egl_display, attribs_layered, &p_gldisplay.egl_config, 1, &config_count);
  139. } else {
  140. eglChooseConfig(p_gldisplay.egl_display, attribs, &p_gldisplay.egl_config, 1, &config_count);
  141. }
  142. ERR_FAIL_COND_V(eglGetError() != EGL_SUCCESS, ERR_BUG);
  143. ERR_FAIL_COND_V(config_count == 0, ERR_UNCONFIGURED);
  144. Vector<EGLint> context_attribs = _get_platform_context_attribs();
  145. p_gldisplay.egl_context = eglCreateContext(p_gldisplay.egl_display, p_gldisplay.egl_config, EGL_NO_CONTEXT, (context_attribs.size() > 0) ? context_attribs.ptr() : nullptr);
  146. 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()));
  147. return OK;
  148. }
  149. Error EGLManager::open_display(void *p_display) {
  150. int gldisplay_id = _get_gldisplay_id(p_display);
  151. if (gldisplay_id < 0) {
  152. return ERR_CANT_CREATE;
  153. } else {
  154. return OK;
  155. }
  156. }
  157. int EGLManager::display_get_native_visual_id(void *p_display) {
  158. int gldisplay_id = _get_gldisplay_id(p_display);
  159. ERR_FAIL_COND_V(gldisplay_id < 0, ERR_CANT_CREATE);
  160. GLDisplay gldisplay = displays[gldisplay_id];
  161. EGLint native_visual_id = -1;
  162. if (!eglGetConfigAttrib(gldisplay.egl_display, gldisplay.egl_config, EGL_NATIVE_VISUAL_ID, &native_visual_id)) {
  163. ERR_FAIL_V(-1);
  164. }
  165. return native_visual_id;
  166. }
  167. Error EGLManager::window_create(DisplayServer::WindowID p_window_id, void *p_display, void *p_native_window, int p_width, int p_height) {
  168. int gldisplay_id = _get_gldisplay_id(p_display);
  169. ERR_FAIL_COND_V(gldisplay_id < 0, ERR_CANT_CREATE);
  170. GLDisplay &gldisplay = displays[gldisplay_id];
  171. // In order to ensure a fast lookup, make sure we got enough elements in the
  172. // windows local vector to use the window id as an index.
  173. if (p_window_id >= (int)windows.size()) {
  174. windows.resize(p_window_id + 1);
  175. }
  176. GLWindow &glwindow = windows[p_window_id];
  177. glwindow.gldisplay_id = gldisplay_id;
  178. if (GLAD_EGL_VERSION_1_5) {
  179. glwindow.egl_surface = eglCreatePlatformWindowSurface(gldisplay.egl_display, gldisplay.egl_config, p_native_window, nullptr);
  180. } else {
  181. EGLNativeWindowType *native_window_type = (EGLNativeWindowType *)p_native_window;
  182. glwindow.egl_surface = eglCreateWindowSurface(gldisplay.egl_display, gldisplay.egl_config, *native_window_type, nullptr);
  183. }
  184. if (glwindow.egl_surface == EGL_NO_SURFACE) {
  185. return ERR_CANT_CREATE;
  186. }
  187. glwindow.initialized = true;
  188. window_make_current(p_window_id);
  189. return OK;
  190. }
  191. void EGLManager::window_destroy(DisplayServer::WindowID p_window_id) {
  192. ERR_FAIL_INDEX(p_window_id, (int)windows.size());
  193. GLWindow &glwindow = windows[p_window_id];
  194. if (!glwindow.initialized) {
  195. return;
  196. }
  197. glwindow.initialized = false;
  198. ERR_FAIL_INDEX(glwindow.gldisplay_id, (int)displays.size());
  199. GLDisplay &gldisplay = displays[glwindow.gldisplay_id];
  200. if (glwindow.egl_surface != EGL_NO_SURFACE) {
  201. eglDestroySurface(gldisplay.egl_display, glwindow.egl_surface);
  202. glwindow.egl_surface = nullptr;
  203. }
  204. }
  205. void EGLManager::release_current() {
  206. if (!current_window) {
  207. return;
  208. }
  209. GLDisplay &current_display = displays[current_window->gldisplay_id];
  210. eglMakeCurrent(current_display.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  211. }
  212. void EGLManager::make_current() {
  213. if (!current_window) {
  214. return;
  215. }
  216. if (!current_window->initialized) {
  217. WARN_PRINT("Current OpenGL window is uninitialized!");
  218. return;
  219. }
  220. GLDisplay &current_display = displays[current_window->gldisplay_id];
  221. eglMakeCurrent(current_display.egl_display, current_window->egl_surface, current_window->egl_surface, current_display.egl_context);
  222. }
  223. void EGLManager::swap_buffers() {
  224. if (!current_window) {
  225. return;
  226. }
  227. if (!current_window->initialized) {
  228. WARN_PRINT("Current OpenGL window is uninitialized!");
  229. return;
  230. }
  231. GLDisplay &current_display = displays[current_window->gldisplay_id];
  232. eglSwapBuffers(current_display.egl_display, current_window->egl_surface);
  233. }
  234. void EGLManager::window_make_current(DisplayServer::WindowID p_window_id) {
  235. if (p_window_id == DisplayServer::INVALID_WINDOW_ID) {
  236. return;
  237. }
  238. GLWindow &glwindow = windows[p_window_id];
  239. if (&glwindow == current_window || !glwindow.initialized) {
  240. return;
  241. }
  242. current_window = &glwindow;
  243. GLDisplay &current_display = displays[current_window->gldisplay_id];
  244. eglMakeCurrent(current_display.egl_display, current_window->egl_surface, current_window->egl_surface, current_display.egl_context);
  245. }
  246. void EGLManager::set_use_vsync(bool p_use) {
  247. // We need an active window to get a display to set the vsync.
  248. if (!current_window) {
  249. return;
  250. }
  251. GLDisplay &disp = displays[current_window->gldisplay_id];
  252. int swap_interval = p_use ? 1 : 0;
  253. if (!eglSwapInterval(disp.egl_display, swap_interval)) {
  254. WARN_PRINT("Could not set V-Sync mode.");
  255. }
  256. use_vsync = p_use;
  257. }
  258. bool EGLManager::is_using_vsync() const {
  259. return use_vsync;
  260. }
  261. EGLContext EGLManager::get_context(DisplayServer::WindowID p_window_id) {
  262. GLWindow &glwindow = windows[p_window_id];
  263. if (!glwindow.initialized) {
  264. return EGL_NO_CONTEXT;
  265. }
  266. GLDisplay &display = displays[glwindow.gldisplay_id];
  267. return display.egl_context;
  268. }
  269. Error EGLManager::initialize() {
  270. #if defined(GLAD_ENABLED) && !defined(EGL_STATIC)
  271. // Passing a null display loads just the bare minimum to create one. We'll have
  272. // to create a temporary test display and reload EGL with it to get a good idea
  273. // of what version is supported on this machine. Currently we're looking for
  274. // 1.5, the latest at the time of writing, which is actually pretty old.
  275. if (!gladLoaderLoadEGL(nullptr)) {
  276. ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Can't load EGL.");
  277. }
  278. // NOTE: EGL_DEFAULT_DISPLAY returns whatever the O.S. deems suitable. I have
  279. // no idea if this may cause problems with multiple display servers and if we
  280. // should handle different EGL contexts in another way.
  281. EGLDisplay tmp_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  282. ERR_FAIL_COND_V(tmp_display == EGL_NO_DISPLAY, ERR_UNAVAILABLE);
  283. eglInitialize(tmp_display, nullptr, nullptr);
  284. int version = gladLoaderLoadEGL(tmp_display);
  285. ERR_FAIL_COND_V_MSG(!version, ERR_UNAVAILABLE, "Can't load EGL.");
  286. print_verbose(vformat("Loaded EGL %d.%d", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)));
  287. ERR_FAIL_COND_V_MSG(!GLAD_EGL_VERSION_1_4, ERR_UNAVAILABLE, "EGL version is too old!");
  288. eglTerminate(tmp_display);
  289. #endif
  290. #ifdef EGL_ANDROID_blob_cache
  291. shader_cache_dir = Engine::get_singleton()->get_shader_cache_path();
  292. if (shader_cache_dir.is_empty()) {
  293. shader_cache_dir = "user://";
  294. }
  295. Error err = OK;
  296. Ref<DirAccess> da = DirAccess::open(shader_cache_dir);
  297. if (da.is_null()) {
  298. ERR_PRINT("EGL: Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
  299. shader_cache_dir = String();
  300. } else {
  301. err = da->change_dir(String("shader_cache").path_join("EGL"));
  302. if (err != OK) {
  303. err = da->make_dir_recursive(String("shader_cache").path_join("EGL"));
  304. }
  305. if (err != OK) {
  306. ERR_PRINT("EGL: Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
  307. shader_cache_dir = String();
  308. } else {
  309. shader_cache_dir = shader_cache_dir.path_join(String("shader_cache").path_join("EGL"));
  310. }
  311. }
  312. #endif
  313. String extensions_string = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
  314. // The above method should always work. If it doesn't, something's very wrong.
  315. ERR_FAIL_COND_V(eglGetError() != EGL_SUCCESS, ERR_BUG);
  316. const char *platform = _get_platform_extension_name();
  317. if (extensions_string.split(" ").find(platform) < 0) {
  318. ERR_FAIL_V_MSG(ERR_UNAVAILABLE, vformat("EGL platform extension \"%s\" not found.", platform));
  319. }
  320. return OK;
  321. }
  322. EGLManager::EGLManager() {
  323. }
  324. EGLManager::~EGLManager() {
  325. for (unsigned int i = 0; i < displays.size(); i++) {
  326. eglTerminate(displays[i].egl_display);
  327. }
  328. }
  329. #endif // EGL_ENABLED