| | |
| | | /* |
| | | * Simple DirectMedia Layer |
| | | * Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> |
| | | * Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> |
| | | * |
| | | * This software is provided 'as-is', without any express or implied |
| | | * warranty. In no event will the authors be held liable for any damages |
| | |
| | | #endif |
| | | #if SDL_VIDEO_DRIVER_ANDROID |
| | | #include <android/native_window.h> |
| | | #include "../core/android/SDL_android.h" |
| | | #endif |
| | | |
| | | #include "SDL_sysvideo.h" |
| | |
| | | #define DEFAULT_OGL_ES "libGLESv1_CM.so.1" |
| | | #endif /* SDL_VIDEO_DRIVER_RPI */ |
| | | |
| | | /** If we happen to not have this defined because of an older EGL version, just define it 0x0 |
| | | as eglGetPlatformDisplayEXT will most likely be NULL if this is missing |
| | | */ |
| | | #ifndef EGL_PLATFORM_DEVICE_EXT |
| | | #define EGL_PLATFORM_DEVICE_EXT 0x0 |
| | | #endif |
| | | |
| | | #ifdef SDL_VIDEO_STATIC_ANGLE |
| | | #define LOAD_FUNC(NAME) \ |
| | | _this->egl_data->NAME = (void *)NAME; |
| | |
| | | return SDL_SetError("Could not retrieve EGL function " #NAME); \ |
| | | } |
| | | #endif |
| | | |
| | | /* it is allowed to not have some of the EGL extensions on start - attempts to use them will fail later. */ |
| | | #define LOAD_FUNC_EGLEXT(NAME) \ |
| | | _this->egl_data->NAME = _this->egl_data->eglGetProcAddress(#NAME); |
| | | |
| | | |
| | | static const char * SDL_EGL_GetErrorName(EGLint eglErrorCode) |
| | | { |
| | |
| | | void * |
| | | SDL_EGL_GetProcAddress(_THIS, const char *proc) |
| | | { |
| | | static char procname[1024]; |
| | | void *retval; |
| | | |
| | | /* eglGetProcAddress is busted on Android http://code.google.com/p/android/issues/detail?id=7681 */ |
| | | #if !defined(SDL_VIDEO_DRIVER_ANDROID) |
| | | if (_this->egl_data->eglGetProcAddress) { |
| | | const Uint32 eglver = (((Uint32) _this->egl_data->egl_version_major) << 16) | ((Uint32) _this->egl_data->egl_version_minor); |
| | | const SDL_bool is_egl_15_or_later = eglver >= ((((Uint32) 1) << 16) | 5); |
| | | void *retval = NULL; |
| | | |
| | | /* EGL 1.5 can use eglGetProcAddress() for any symbol. 1.4 and earlier can't use it for core entry points. */ |
| | | if (!retval && is_egl_15_or_later && _this->egl_data->eglGetProcAddress) { |
| | | retval = _this->egl_data->eglGetProcAddress(proc); |
| | | } |
| | | |
| | | /* Try SDL_LoadFunction() first for EGL <= 1.4, or as a fallback for >= 1.5. */ |
| | | if (!retval) { |
| | | static char procname[64]; |
| | | retval = SDL_LoadFunction(_this->egl_data->egl_dll_handle, proc); |
| | | /* just in case you need an underscore prepended... */ |
| | | if (!retval && (SDL_strlen(proc) < (sizeof (procname) - 1))) { |
| | | procname[0] = '_'; |
| | | SDL_strlcpy(procname + 1, proc, sizeof (procname) - 1); |
| | | retval = SDL_LoadFunction(_this->egl_data->egl_dll_handle, procname); |
| | | } |
| | | } |
| | | |
| | | /* Try eglGetProcAddress if we on <= 1.4 and still searching... */ |
| | | if (!retval && !is_egl_15_or_later && _this->egl_data->eglGetProcAddress) { |
| | | retval = _this->egl_data->eglGetProcAddress(proc); |
| | | if (retval) { |
| | | return retval; |
| | | } |
| | | } |
| | | #endif |
| | | |
| | | retval = SDL_LoadFunction(_this->egl_data->egl_dll_handle, proc); |
| | | if (!retval && SDL_strlen(proc) <= 1022) { |
| | | procname[0] = '_'; |
| | | SDL_strlcpy(procname + 1, proc, 1022); |
| | | retval = SDL_LoadFunction(_this->egl_data->egl_dll_handle, procname); |
| | | } |
| | | |
| | | return retval; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | int |
| | | SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_display, EGLenum platform) |
| | | SDL_EGL_LoadLibraryOnly(_THIS, const char *egl_path) |
| | | { |
| | | void *dll_handle = NULL, *egl_dll_handle = NULL; /* The naming is counter intuitive, but hey, I just work here -- Gabriel */ |
| | | const char *path = NULL; |
| | | int egl_version_major = 0, egl_version_minor = 0; |
| | | #if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT |
| | | const char *d3dcompiler; |
| | | #endif |
| | |
| | | |
| | | #if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT |
| | | d3dcompiler = SDL_GetHint(SDL_HINT_VIDEO_WIN_D3DCOMPILER); |
| | | if (!d3dcompiler) { |
| | | if (WIN_IsWindowsVistaOrGreater()) { |
| | | d3dcompiler = "d3dcompiler_46.dll"; |
| | | } else { |
| | | d3dcompiler = "d3dcompiler_43.dll"; |
| | | if (d3dcompiler) { |
| | | if (SDL_strcasecmp(d3dcompiler, "none") != 0) { |
| | | if (SDL_LoadObject(d3dcompiler) == NULL) { |
| | | SDL_ClearError(); |
| | | } |
| | | } |
| | | } |
| | | if (SDL_strcasecmp(d3dcompiler, "none") != 0) { |
| | | if (SDL_LoadObject(d3dcompiler) == NULL) { |
| | | SDL_ClearError(); |
| | | } else { |
| | | if (WIN_IsWindowsVistaOrGreater()) { |
| | | /* Try the newer d3d compilers first */ |
| | | const char *d3dcompiler_list[] = { |
| | | "d3dcompiler_47.dll", "d3dcompiler_46.dll", |
| | | }; |
| | | int i; |
| | | |
| | | for (i = 0; i < SDL_arraysize(d3dcompiler_list); ++i) { |
| | | if (SDL_LoadObject(d3dcompiler_list[i]) != NULL) { |
| | | break; |
| | | } |
| | | SDL_ClearError(); |
| | | } |
| | | } else { |
| | | if (SDL_LoadObject("d3dcompiler_43.dll") == NULL) { |
| | | SDL_ClearError(); |
| | | } |
| | | } |
| | | } |
| | | #endif |
| | |
| | | LOAD_FUNC(eglWaitNative); |
| | | LOAD_FUNC(eglWaitGL); |
| | | LOAD_FUNC(eglBindAPI); |
| | | LOAD_FUNC(eglQueryAPI); |
| | | LOAD_FUNC(eglQueryString); |
| | | LOAD_FUNC(eglGetError); |
| | | LOAD_FUNC_EGLEXT(eglQueryDevicesEXT); |
| | | LOAD_FUNC_EGLEXT(eglGetPlatformDisplayEXT); |
| | | |
| | | _this->gl_config.driver_loaded = 1; |
| | | |
| | | if (path) { |
| | | SDL_strlcpy(_this->gl_config.driver_path, path, sizeof(_this->gl_config.driver_path) - 1); |
| | | } else { |
| | | *_this->gl_config.driver_path = '\0'; |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | static void |
| | | SDL_EGL_GetVersion(_THIS) { |
| | | if (_this->egl_data->eglQueryString) { |
| | | /* EGL 1.5 allows querying for client version */ |
| | | const char *egl_version = _this->egl_data->eglQueryString(EGL_NO_DISPLAY, EGL_VERSION); |
| | | if (egl_version != NULL) { |
| | | if (SDL_sscanf(egl_version, "%d.%d", &egl_version_major, &egl_version_minor) != 2) { |
| | | egl_version_major = 0; |
| | | egl_version_minor = 0; |
| | | const char *egl_version = _this->egl_data->eglQueryString(_this->egl_data->egl_display, EGL_VERSION); |
| | | if (egl_version) { |
| | | int major = 0, minor = 0; |
| | | if (SDL_sscanf(egl_version, "%d.%d", &major, &minor) == 2) { |
| | | _this->egl_data->egl_version_major = major; |
| | | _this->egl_data->egl_version_minor = minor; |
| | | } else { |
| | | SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "Could not parse EGL version string: %s", egl_version); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | _this->egl_data->egl_version_major = egl_version_major; |
| | | _this->egl_data->egl_version_minor = egl_version_minor; |
| | | int |
| | | SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_display, EGLenum platform) |
| | | { |
| | | int egl_version_major, egl_version_minor; |
| | | int library_load_retcode = SDL_EGL_LoadLibraryOnly(_this, egl_path); |
| | | if (library_load_retcode != 0) { |
| | | return library_load_retcode; |
| | | } |
| | | |
| | | /* EGL 1.5 allows querying for client version with EGL_NO_DISPLAY */ |
| | | SDL_EGL_GetVersion(_this); |
| | | |
| | | egl_version_major = _this->egl_data->egl_version_major; |
| | | egl_version_minor = _this->egl_data->egl_version_minor; |
| | | |
| | | if (egl_version_major == 1 && egl_version_minor == 5) { |
| | | LOAD_FUNC(eglGetPlatformDisplay); |
| | |
| | | _this->egl_data->egl_display = _this->egl_data->eglGetDisplay(native_display); |
| | | } |
| | | if (_this->egl_data->egl_display == EGL_NO_DISPLAY) { |
| | | _this->gl_config.driver_loaded = 0; |
| | | *_this->gl_config.driver_path = '\0'; |
| | | return SDL_SetError("Could not get EGL display"); |
| | | } |
| | | |
| | | if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) { |
| | | _this->gl_config.driver_loaded = 0; |
| | | *_this->gl_config.driver_path = '\0'; |
| | | return SDL_SetError("Could not initialize EGL"); |
| | | } |
| | | #endif |
| | | |
| | | if (path) { |
| | | SDL_strlcpy(_this->gl_config.driver_path, path, sizeof(_this->gl_config.driver_path) - 1); |
| | | } else { |
| | | *_this->gl_config.driver_path = '\0'; |
| | | } |
| | | |
| | | /* Get the EGL version with a valid egl_display, for EGL <= 1.4 */ |
| | | SDL_EGL_GetVersion(_this); |
| | | |
| | | _this->egl_data->is_offscreen = 0; |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | /** |
| | | On multi GPU machines EGL device 0 is not always the first valid GPU. |
| | | Container environments can restrict access to some GPUs that are still listed in the EGL |
| | | device list. If the requested device is a restricted GPU and cannot be used |
| | | (eglInitialize() will fail) then attempt to automatically and silently select the next |
| | | valid available GPU for EGL to use. |
| | | */ |
| | | |
| | | int |
| | | SDL_EGL_InitializeOffscreen(_THIS, int device) |
| | | { |
| | | void *egl_devices[SDL_EGL_MAX_DEVICES]; |
| | | EGLint num_egl_devices = 0; |
| | | const char *egl_device_hint; |
| | | |
| | | if (_this->gl_config.driver_loaded != 1) { |
| | | return SDL_SetError("SDL_EGL_LoadLibraryOnly() has not been called or has failed."); |
| | | } |
| | | |
| | | /* Check for all extensions that are optional until used and fail if any is missing */ |
| | | if (_this->egl_data->eglQueryDevicesEXT == NULL) { |
| | | return SDL_SetError("eglQueryDevicesEXT is missing (EXT_device_enumeration not supported by the drivers?)"); |
| | | } |
| | | |
| | | if (_this->egl_data->eglGetPlatformDisplayEXT == NULL) { |
| | | return SDL_SetError("eglGetPlatformDisplayEXT is missing (EXT_platform_base not supported by the drivers?)"); |
| | | } |
| | | |
| | | if (_this->egl_data->eglQueryDevicesEXT(SDL_EGL_MAX_DEVICES, egl_devices, &num_egl_devices) != EGL_TRUE) { |
| | | return SDL_SetError("eglQueryDevicesEXT() failed"); |
| | | } |
| | | |
| | | egl_device_hint = SDL_GetHint("SDL_HINT_EGL_DEVICE"); |
| | | if (egl_device_hint) { |
| | | device = SDL_atoi(egl_device_hint); |
| | | |
| | | if (device >= num_egl_devices) { |
| | | return SDL_SetError("Invalid EGL device is requested."); |
| | | } |
| | | |
| | | _this->egl_data->egl_display = _this->egl_data->eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, egl_devices[device], NULL); |
| | | |
| | | if (_this->egl_data->egl_display == EGL_NO_DISPLAY) { |
| | | return SDL_SetError("eglGetPlatformDisplayEXT() failed."); |
| | | } |
| | | |
| | | if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) { |
| | | return SDL_SetError("Could not initialize EGL"); |
| | | } |
| | | } |
| | | else { |
| | | int i; |
| | | SDL_bool found = SDL_FALSE; |
| | | EGLDisplay attempted_egl_display; |
| | | |
| | | /* If no hint is provided lets look for the first device/display that will allow us to eglInit */ |
| | | for (i = 0; i < num_egl_devices; i++) { |
| | | attempted_egl_display = _this->egl_data->eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, egl_devices[i], NULL); |
| | | |
| | | if (attempted_egl_display == EGL_NO_DISPLAY) { |
| | | continue; |
| | | } |
| | | |
| | | if (_this->egl_data->eglInitialize(attempted_egl_display, NULL, NULL) != EGL_TRUE) { |
| | | _this->egl_data->eglTerminate(attempted_egl_display); |
| | | continue; |
| | | } |
| | | |
| | | /* We did not fail, we'll pick this one! */ |
| | | _this->egl_data->egl_display = attempted_egl_display; |
| | | found = SDL_TRUE; |
| | | |
| | | break; |
| | | } |
| | | |
| | | if (!found) { |
| | | return SDL_SetError("Could not find a valid EGL device to initialize"); |
| | | } |
| | | } |
| | | |
| | | /* Get the EGL version with a valid egl_display, for EGL <= 1.4 */ |
| | | SDL_EGL_GetVersion(_this); |
| | | |
| | | _this->egl_data->is_offscreen = 1; |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | void |
| | | SDL_EGL_SetRequiredVisualId(_THIS, int visual_id) |
| | | { |
| | | _this->egl_data->egl_required_visual_id=visual_id; |
| | | } |
| | | |
| | | #ifdef DUMP_EGL_CONFIG |
| | |
| | | /* 64 seems nice. */ |
| | | EGLint attribs[64]; |
| | | EGLint found_configs = 0, value; |
| | | #ifdef SDL_VIDEO_DRIVER_KMSDRM |
| | | /* Intel EGL on KMS/DRM (al least) returns invalid configs that confuse the bitdiff search used */ |
| | | /* later in this function, so we simply use the first one when using the KMSDRM driver for now. */ |
| | | EGLConfig configs[1]; |
| | | #else |
| | | /* 128 seems even nicer here */ |
| | | EGLConfig configs[128]; |
| | | #endif |
| | | int i, j, best_bitdiff = -1, bitdiff; |
| | | |
| | | if (!_this->egl_data) { |
| | |
| | | attribs[i++] = _this->gl_config.multisamplesamples; |
| | | } |
| | | |
| | | if (_this->egl_data->is_offscreen) { |
| | | attribs[i++] = EGL_SURFACE_TYPE; |
| | | attribs[i++] = EGL_PBUFFER_BIT; |
| | | } |
| | | |
| | | attribs[i++] = EGL_RENDERABLE_TYPE; |
| | | if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) { |
| | | #ifdef EGL_KHR_create_context |
| | |
| | | /* From those, we select the one that matches our requirements more closely via a makeshift algorithm */ |
| | | |
| | | for (i = 0; i < found_configs; i++ ) { |
| | | if (_this->egl_data->egl_required_visual_id) |
| | | { |
| | | EGLint format; |
| | | _this->egl_data->eglGetConfigAttrib(_this->egl_data->egl_display, |
| | | configs[i], |
| | | EGL_NATIVE_VISUAL_ID, &format); |
| | | if (_this->egl_data->egl_required_visual_id != format) |
| | | continue; |
| | | } |
| | | |
| | | bitdiff = 0; |
| | | for (j = 0; j < SDL_arraysize(attribs) - 1; j += 2) { |
| | | if (attribs[j] == EGL_NONE) { |
| | |
| | | EGL_NATIVE_VISUAL_ID, &format); |
| | | |
| | | ANativeWindow_setBuffersGeometry(nw, 0, 0, format); |
| | | |
| | | /* Update SurfaceView holder format. |
| | | * May triggers a sequence surfaceDestroyed(), surfaceCreated(), surfaceChanged(). */ |
| | | Android_JNI_SetSurfaceViewFormat(format); |
| | | } |
| | | #endif |
| | | if (_this->gl_config.framebuffer_srgb_capable) { |
| | |
| | | return surface; |
| | | } |
| | | |
| | | EGLSurface |
| | | SDL_EGL_CreateOffscreenSurface(_THIS, int width, int height) |
| | | { |
| | | EGLint attributes[] = { |
| | | EGL_WIDTH, width, |
| | | EGL_HEIGHT, height, |
| | | EGL_NONE |
| | | }; |
| | | |
| | | if (SDL_EGL_ChooseConfig(_this) != 0) { |
| | | return EGL_NO_SURFACE; |
| | | } |
| | | |
| | | return _this->egl_data->eglCreatePbufferSurface( |
| | | _this->egl_data->egl_display, |
| | | _this->egl_data->egl_config, |
| | | attributes); |
| | | } |
| | | |
| | | void |
| | | SDL_EGL_DestroySurface(_THIS, EGLSurface egl_surface) |
| | | { |