SDL_egl.c (18598B)
1/* 2 * Simple DirectMedia Layer 3 * Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org> 4 * 5 * This software is provided 'as-is', without any express or implied 6 * warranty. In no event will the authors be held liable for any damages 7 * arising from the use of this software. 8 * 9 * Permission is granted to anyone to use this software for any purpose, 10 * including commercial applications, and to alter it and redistribute it 11 * freely, subject to the following restrictions: 12 * 13 * 1. The origin of this software must not be misrepresented; you must not 14 * claim that you wrote the original software. If you use this software 15 * in a product, an acknowledgment in the product documentation would be 16 * appreciated but is not required. 17 * 2. Altered source versions must be plainly marked as such, and must not be 18 * misrepresented as being the original software. 19 * 3. This notice may not be removed or altered from any source distribution. 20 */ 21#include "../SDL_internal.h" 22 23#if SDL_VIDEO_OPENGL_EGL 24 25#if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT 26#include "../core/windows/SDL_windows.h" 27#endif 28 29#include "SDL_sysvideo.h" 30#include "SDL_egl_c.h" 31#include "SDL_loadso.h" 32#include "SDL_hints.h" 33 34#if SDL_VIDEO_DRIVER_RPI 35/* Raspbian places the OpenGL ES/EGL binaries in a non standard path */ 36#define DEFAULT_EGL "/opt/vc/lib/libEGL.so" 37#define DEFAULT_OGL_ES2 "/opt/vc/lib/libGLESv2.so" 38#define DEFAULT_OGL_ES_PVR "/opt/vc/lib/libGLES_CM.so" 39#define DEFAULT_OGL_ES "/opt/vc/lib/libGLESv1_CM.so" 40 41#elif SDL_VIDEO_DRIVER_ANDROID || SDL_VIDEO_DRIVER_VIVANTE 42/* Android */ 43#define DEFAULT_EGL "libEGL.so" 44#define DEFAULT_OGL_ES2 "libGLESv2.so" 45#define DEFAULT_OGL_ES_PVR "libGLES_CM.so" 46#define DEFAULT_OGL_ES "libGLESv1_CM.so" 47 48#elif SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT 49/* EGL AND OpenGL ES support via ANGLE */ 50#define DEFAULT_EGL "libEGL.dll" 51#define DEFAULT_OGL_ES2 "libGLESv2.dll" 52#define DEFAULT_OGL_ES_PVR "libGLES_CM.dll" 53#define DEFAULT_OGL_ES "libGLESv1_CM.dll" 54 55#else 56/* Desktop Linux */ 57#define DEFAULT_OGL "libGL.so.1" 58#define DEFAULT_EGL "libEGL.so.1" 59#define DEFAULT_OGL_ES2 "libGLESv2.so.2" 60#define DEFAULT_OGL_ES_PVR "libGLES_CM.so.1" 61#define DEFAULT_OGL_ES "libGLESv1_CM.so.1" 62#endif /* SDL_VIDEO_DRIVER_RPI */ 63 64#define LOAD_FUNC(NAME) \ 65_this->egl_data->NAME = SDL_LoadFunction(_this->egl_data->dll_handle, #NAME); \ 66if (!_this->egl_data->NAME) \ 67{ \ 68 return SDL_SetError("Could not retrieve EGL function " #NAME); \ 69} 70 71/* EGL implementation of SDL OpenGL ES support */ 72#ifdef EGL_KHR_create_context 73static int SDL_EGL_HasExtension(_THIS, const char *ext) 74{ 75 int i; 76 int len = 0; 77 int ext_len; 78 const char *exts; 79 const char *ext_word; 80 81 ext_len = SDL_strlen(ext); 82 exts = _this->egl_data->eglQueryString(_this->egl_data->egl_display, EGL_EXTENSIONS); 83 84 if(exts) { 85 ext_word = exts; 86 87 for(i = 0; exts[i] != 0; i++) { 88 if(exts[i] == ' ') { 89 if(ext_len == len && !SDL_strncmp(ext_word, ext, len)) { 90 return 1; 91 } 92 93 len = 0; 94 ext_word = &exts[i + 1]; 95 } 96 else { 97 len++; 98 } 99 } 100 } 101 102 return 0; 103} 104#endif /* EGL_KHR_create_context */ 105 106void * 107SDL_EGL_GetProcAddress(_THIS, const char *proc) 108{ 109 static char procname[1024]; 110 void *retval; 111 112 /* eglGetProcAddress is busted on Android http://code.google.com/p/android/issues/detail?id=7681 */ 113#if !defined(SDL_VIDEO_DRIVER_ANDROID) && !defined(SDL_VIDEO_DRIVER_MIR) 114 if (_this->egl_data->eglGetProcAddress) { 115 retval = _this->egl_data->eglGetProcAddress(proc); 116 if (retval) { 117 return retval; 118 } 119 } 120#endif 121 122 retval = SDL_LoadFunction(_this->egl_data->egl_dll_handle, proc); 123 if (!retval && SDL_strlen(proc) <= 1022) { 124 procname[0] = '_'; 125 SDL_strlcpy(procname + 1, proc, 1022); 126 retval = SDL_LoadFunction(_this->egl_data->egl_dll_handle, procname); 127 } 128 return retval; 129} 130 131void 132SDL_EGL_UnloadLibrary(_THIS) 133{ 134 if (_this->egl_data) { 135 if (_this->egl_data->egl_display) { 136 _this->egl_data->eglTerminate(_this->egl_data->egl_display); 137 _this->egl_data->egl_display = NULL; 138 } 139 140 if (_this->egl_data->dll_handle) { 141 SDL_UnloadObject(_this->egl_data->dll_handle); 142 _this->egl_data->dll_handle = NULL; 143 } 144 if (_this->egl_data->egl_dll_handle) { 145 SDL_UnloadObject(_this->egl_data->egl_dll_handle); 146 _this->egl_data->egl_dll_handle = NULL; 147 } 148 149 SDL_free(_this->egl_data); 150 _this->egl_data = NULL; 151 } 152} 153 154int 155SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_display) 156{ 157 void *dll_handle = NULL, *egl_dll_handle = NULL; /* The naming is counter intuitive, but hey, I just work here -- Gabriel */ 158 char *path = NULL; 159#if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT 160 const char *d3dcompiler; 161#endif 162 163 if (_this->egl_data) { 164 return SDL_SetError("OpenGL ES context already created"); 165 } 166 167 _this->egl_data = (struct SDL_EGL_VideoData *) SDL_calloc(1, sizeof(SDL_EGL_VideoData)); 168 if (!_this->egl_data) { 169 return SDL_OutOfMemory(); 170 } 171 172#if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT 173 d3dcompiler = SDL_GetHint(SDL_HINT_VIDEO_WIN_D3DCOMPILER); 174 if (!d3dcompiler) { 175 if (WIN_IsWindowsVistaOrGreater()) { 176 d3dcompiler = "d3dcompiler_46.dll"; 177 } else { 178 d3dcompiler = "d3dcompiler_43.dll"; 179 } 180 } 181 if (SDL_strcasecmp(d3dcompiler, "none") != 0) { 182 SDL_LoadObject(d3dcompiler); 183 } 184#endif 185 186 /* A funny thing, loading EGL.so first does not work on the Raspberry, so we load libGL* first */ 187 path = SDL_getenv("SDL_VIDEO_GL_DRIVER"); 188 if (path != NULL) { 189 egl_dll_handle = SDL_LoadObject(path); 190 } 191 192 if (egl_dll_handle == NULL) { 193 if(_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) { 194 if (_this->gl_config.major_version > 1) { 195 path = DEFAULT_OGL_ES2; 196 egl_dll_handle = SDL_LoadObject(path); 197 } 198 else { 199 path = DEFAULT_OGL_ES; 200 egl_dll_handle = SDL_LoadObject(path); 201 if (egl_dll_handle == NULL) { 202 path = DEFAULT_OGL_ES_PVR; 203 egl_dll_handle = SDL_LoadObject(path); 204 } 205 } 206 } 207#ifdef DEFAULT_OGL 208 else { 209 path = DEFAULT_OGL; 210 egl_dll_handle = SDL_LoadObject(path); 211 } 212#endif 213 } 214 _this->egl_data->egl_dll_handle = egl_dll_handle; 215 216 if (egl_dll_handle == NULL) { 217 return SDL_SetError("Could not initialize OpenGL / GLES library"); 218 } 219 220 /* Loading libGL* in the previous step took care of loading libEGL.so, but we future proof by double checking */ 221 if (egl_path != NULL) { 222 dll_handle = SDL_LoadObject(egl_path); 223 } 224 /* Try loading a EGL symbol, if it does not work try the default library paths */ 225 if (dll_handle == NULL || SDL_LoadFunction(dll_handle, "eglChooseConfig") == NULL) { 226 if (dll_handle != NULL) { 227 SDL_UnloadObject(dll_handle); 228 } 229 path = SDL_getenv("SDL_VIDEO_EGL_DRIVER"); 230 if (path == NULL) { 231 path = DEFAULT_EGL; 232 } 233 dll_handle = SDL_LoadObject(path); 234 if (dll_handle == NULL || SDL_LoadFunction(dll_handle, "eglChooseConfig") == NULL) { 235 if (dll_handle != NULL) { 236 SDL_UnloadObject(dll_handle); 237 } 238 return SDL_SetError("Could not load EGL library"); 239 } 240 SDL_ClearError(); 241 } 242 243 _this->egl_data->dll_handle = dll_handle; 244 245 /* Load new function pointers */ 246 LOAD_FUNC(eglGetDisplay); 247 LOAD_FUNC(eglInitialize); 248 LOAD_FUNC(eglTerminate); 249 LOAD_FUNC(eglGetProcAddress); 250 LOAD_FUNC(eglChooseConfig); 251 LOAD_FUNC(eglGetConfigAttrib); 252 LOAD_FUNC(eglCreateContext); 253 LOAD_FUNC(eglDestroyContext); 254 LOAD_FUNC(eglCreateWindowSurface); 255 LOAD_FUNC(eglDestroySurface); 256 LOAD_FUNC(eglMakeCurrent); 257 LOAD_FUNC(eglSwapBuffers); 258 LOAD_FUNC(eglSwapInterval); 259 LOAD_FUNC(eglWaitNative); 260 LOAD_FUNC(eglWaitGL); 261 LOAD_FUNC(eglBindAPI); 262 LOAD_FUNC(eglQueryString); 263 264#if !defined(__WINRT__) 265 _this->egl_data->egl_display = _this->egl_data->eglGetDisplay(native_display); 266 if (!_this->egl_data->egl_display) { 267 return SDL_SetError("Could not get EGL display"); 268 } 269 270 if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) { 271 return SDL_SetError("Could not initialize EGL"); 272 } 273#endif 274 275 _this->gl_config.driver_loaded = 1; 276 277 if (path) { 278 SDL_strlcpy(_this->gl_config.driver_path, path, sizeof(_this->gl_config.driver_path) - 1); 279 } else { 280 *_this->gl_config.driver_path = '\0'; 281 } 282 283 return 0; 284} 285 286int 287SDL_EGL_ChooseConfig(_THIS) 288{ 289 /* 64 seems nice. */ 290 EGLint attribs[64]; 291 EGLint found_configs = 0, value; 292 /* 128 seems even nicer here */ 293 EGLConfig configs[128]; 294 int i, j, best_bitdiff = -1, bitdiff; 295 296 if (!_this->egl_data) { 297 /* The EGL library wasn't loaded, SDL_GetError() should have info */ 298 return -1; 299 } 300 301 /* Get a valid EGL configuration */ 302 i = 0; 303 attribs[i++] = EGL_RED_SIZE; 304 attribs[i++] = _this->gl_config.red_size; 305 attribs[i++] = EGL_GREEN_SIZE; 306 attribs[i++] = _this->gl_config.green_size; 307 attribs[i++] = EGL_BLUE_SIZE; 308 attribs[i++] = _this->gl_config.blue_size; 309 310 if (_this->gl_config.alpha_size) { 311 attribs[i++] = EGL_ALPHA_SIZE; 312 attribs[i++] = _this->gl_config.alpha_size; 313 } 314 315 if (_this->gl_config.buffer_size) { 316 attribs[i++] = EGL_BUFFER_SIZE; 317 attribs[i++] = _this->gl_config.buffer_size; 318 } 319 320 attribs[i++] = EGL_DEPTH_SIZE; 321 attribs[i++] = _this->gl_config.depth_size; 322 323 if (_this->gl_config.stencil_size) { 324 attribs[i++] = EGL_STENCIL_SIZE; 325 attribs[i++] = _this->gl_config.stencil_size; 326 } 327 328 if (_this->gl_config.multisamplebuffers) { 329 attribs[i++] = EGL_SAMPLE_BUFFERS; 330 attribs[i++] = _this->gl_config.multisamplebuffers; 331 } 332 333 if (_this->gl_config.multisamplesamples) { 334 attribs[i++] = EGL_SAMPLES; 335 attribs[i++] = _this->gl_config.multisamplesamples; 336 } 337 338 attribs[i++] = EGL_RENDERABLE_TYPE; 339 if(_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) { 340 if (_this->gl_config.major_version == 2) { 341 attribs[i++] = EGL_OPENGL_ES2_BIT; 342 } else { 343 attribs[i++] = EGL_OPENGL_ES_BIT; 344 } 345 _this->egl_data->eglBindAPI(EGL_OPENGL_ES_API); 346 } 347 else { 348 attribs[i++] = EGL_OPENGL_BIT; 349 _this->egl_data->eglBindAPI(EGL_OPENGL_API); 350 } 351 352 attribs[i++] = EGL_NONE; 353 354 if (_this->egl_data->eglChooseConfig(_this->egl_data->egl_display, 355 attribs, 356 configs, SDL_arraysize(configs), 357 &found_configs) == EGL_FALSE || 358 found_configs == 0) { 359 return SDL_SetError("Couldn't find matching EGL config"); 360 } 361 362 /* eglChooseConfig returns a number of configurations that match or exceed the requested attribs. */ 363 /* From those, we select the one that matches our requirements more closely via a makeshift algorithm */ 364 365 for ( i=0; i<found_configs; i++ ) { 366 bitdiff = 0; 367 for (j = 0; j < SDL_arraysize(attribs) - 1; j += 2) { 368 if (attribs[j] == EGL_NONE) { 369 break; 370 } 371 372 if ( attribs[j+1] != EGL_DONT_CARE && ( 373 attribs[j] == EGL_RED_SIZE || 374 attribs[j] == EGL_GREEN_SIZE || 375 attribs[j] == EGL_BLUE_SIZE || 376 attribs[j] == EGL_ALPHA_SIZE || 377 attribs[j] == EGL_DEPTH_SIZE || 378 attribs[j] == EGL_STENCIL_SIZE)) { 379 _this->egl_data->eglGetConfigAttrib(_this->egl_data->egl_display, configs[i], attribs[j], &value); 380 bitdiff += value - attribs[j + 1]; /* value is always >= attrib */ 381 } 382 } 383 384 if (bitdiff < best_bitdiff || best_bitdiff == -1) { 385 _this->egl_data->egl_config = configs[i]; 386 387 best_bitdiff = bitdiff; 388 } 389 390 if (bitdiff == 0) break; /* we found an exact match! */ 391 } 392 393 return 0; 394} 395 396SDL_GLContext 397SDL_EGL_CreateContext(_THIS, EGLSurface egl_surface) 398{ 399 EGLint context_attrib_list[] = { 400 EGL_CONTEXT_CLIENT_VERSION, 401 1, 402 EGL_NONE, 403 EGL_NONE, 404 EGL_NONE, 405 EGL_NONE, 406 EGL_NONE 407 }; 408 409 EGLContext egl_context, share_context = EGL_NO_CONTEXT; 410 411 if (!_this->egl_data) { 412 /* The EGL library wasn't loaded, SDL_GetError() should have info */ 413 return NULL; 414 } 415 416 if (_this->gl_config.share_with_current_context) { 417 share_context = (EGLContext)SDL_GL_GetCurrentContext(); 418 } 419 420 /* Bind the API */ 421 if(_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) { 422 _this->egl_data->eglBindAPI(EGL_OPENGL_ES_API); 423 if (_this->gl_config.major_version) { 424 context_attrib_list[1] = _this->gl_config.major_version; 425 } 426 427 egl_context = _this->egl_data->eglCreateContext(_this->egl_data->egl_display, 428 _this->egl_data->egl_config, 429 share_context, context_attrib_list); 430 } 431 else { 432 _this->egl_data->eglBindAPI(EGL_OPENGL_API); 433#ifdef EGL_KHR_create_context 434 if(SDL_EGL_HasExtension(_this, "EGL_KHR_create_context")) { 435 context_attrib_list[0] = EGL_CONTEXT_MAJOR_VERSION_KHR; 436 context_attrib_list[1] = _this->gl_config.major_version; 437 context_attrib_list[2] = EGL_CONTEXT_MINOR_VERSION_KHR; 438 context_attrib_list[3] = _this->gl_config.minor_version; 439 context_attrib_list[4] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR; 440 switch(_this->gl_config.profile_mask) { 441 case SDL_GL_CONTEXT_PROFILE_COMPATIBILITY: 442 context_attrib_list[5] = EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; 443 break; 444 445 case SDL_GL_CONTEXT_PROFILE_CORE: 446 default: 447 context_attrib_list[5] = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; 448 break; 449 } 450 } 451 else { 452 context_attrib_list[0] = EGL_NONE; 453 } 454#else /* EGL_KHR_create_context */ 455 context_attrib_list[0] = EGL_NONE; 456#endif /* EGL_KHR_create_context */ 457 egl_context = _this->egl_data->eglCreateContext(_this->egl_data->egl_display, 458 _this->egl_data->egl_config, 459 share_context, context_attrib_list); 460 } 461 462 if (egl_context == EGL_NO_CONTEXT) { 463 SDL_SetError("Could not create EGL context"); 464 return NULL; 465 } 466 467 _this->egl_data->egl_swapinterval = 0; 468 469 if (SDL_EGL_MakeCurrent(_this, egl_surface, egl_context) < 0) { 470 SDL_EGL_DeleteContext(_this, egl_context); 471 SDL_SetError("Could not make EGL context current"); 472 return NULL; 473 } 474 475 return (SDL_GLContext) egl_context; 476} 477 478int 479SDL_EGL_MakeCurrent(_THIS, EGLSurface egl_surface, SDL_GLContext context) 480{ 481 EGLContext egl_context = (EGLContext) context; 482 483 if (!_this->egl_data) { 484 return SDL_SetError("OpenGL not initialized"); 485 } 486 487 /* The android emulator crashes badly if you try to eglMakeCurrent 488 * with a valid context and invalid surface, so we have to check for both here. 489 */ 490 if (!egl_context || !egl_surface) { 491 _this->egl_data->eglMakeCurrent(_this->egl_data->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 492 } 493 else { 494 if (!_this->egl_data->eglMakeCurrent(_this->egl_data->egl_display, 495 egl_surface, egl_surface, egl_context)) { 496 return SDL_SetError("Unable to make EGL context current"); 497 } 498 } 499 500 return 0; 501} 502 503int 504SDL_EGL_SetSwapInterval(_THIS, int interval) 505{ 506 EGLBoolean status; 507 508 if (!_this->egl_data) { 509 return SDL_SetError("EGL not initialized"); 510 } 511 512 status = _this->egl_data->eglSwapInterval(_this->egl_data->egl_display, interval); 513 if (status == EGL_TRUE) { 514 _this->egl_data->egl_swapinterval = interval; 515 return 0; 516 } 517 518 return SDL_SetError("Unable to set the EGL swap interval"); 519} 520 521int 522SDL_EGL_GetSwapInterval(_THIS) 523{ 524 if (!_this->egl_data) { 525 return SDL_SetError("EGL not initialized"); 526 } 527 528 return _this->egl_data->egl_swapinterval; 529} 530 531void 532SDL_EGL_SwapBuffers(_THIS, EGLSurface egl_surface) 533{ 534 _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, egl_surface); 535} 536 537void 538SDL_EGL_DeleteContext(_THIS, SDL_GLContext context) 539{ 540 EGLContext egl_context = (EGLContext) context; 541 542 /* Clean up GLES and EGL */ 543 if (!_this->egl_data) { 544 return; 545 } 546 547 if (egl_context != NULL && egl_context != EGL_NO_CONTEXT) { 548 SDL_EGL_MakeCurrent(_this, NULL, NULL); 549 _this->egl_data->eglDestroyContext(_this->egl_data->egl_display, egl_context); 550 } 551 552} 553 554EGLSurface * 555SDL_EGL_CreateSurface(_THIS, NativeWindowType nw) 556{ 557 if (SDL_EGL_ChooseConfig(_this) != 0) { 558 return EGL_NO_SURFACE; 559 } 560 561#if __ANDROID__ 562 { 563 /* Android docs recommend doing this! 564 * Ref: http://developer.android.com/reference/android/app/NativeActivity.html 565 */ 566 EGLint format; 567 _this->egl_data->eglGetConfigAttrib(_this->egl_data->egl_display, 568 _this->egl_data->egl_config, 569 EGL_NATIVE_VISUAL_ID, &format); 570 571 ANativeWindow_setBuffersGeometry(nw, 0, 0, format); 572 } 573#endif 574 575 return _this->egl_data->eglCreateWindowSurface( 576 _this->egl_data->egl_display, 577 _this->egl_data->egl_config, 578 nw, NULL); 579} 580 581void 582SDL_EGL_DestroySurface(_THIS, EGLSurface egl_surface) 583{ 584 if (!_this->egl_data) { 585 return; 586 } 587 588 if (egl_surface != EGL_NO_SURFACE) { 589 _this->egl_data->eglDestroySurface(_this->egl_data->egl_display, egl_surface); 590 } 591} 592 593#endif /* SDL_VIDEO_OPENGL_EGL */ 594 595/* vi: set ts=4 sw=4 expandtab: */ 596