vo_gpu: opengl: make it work with EGL 1.4

This tries to deal with the crazy EGL situation. The summary is:

- using eglGetDisplay() with multiple windowing platforms doesn't really
  work, but Mesa had an awful hack for it
- this hack can be disabled at build time, and some distros sometimes
  accidentally or intentionally do so
- Mesa will probably eventually disable it by default
- we switched to eglGetPlatformDisplay(), but this requires EGL 1.5
- the very regrettable graphics company (also known as Nvidia) ships
  drivers (for old hardware I think) that are EGL 1.4 only
- that means even though we "require" EGL 1.5 and link against it, the
  runtime EGL may be 1.4
- trying to run mpv there crashes in the dynamic linker
- so we have to go through some more awful compatibility hacks

This commit tries to do it "properly", but using EGL 1.4 as base. The
plaform selection mechanism is a messy extension there, which got
elevated to core API in 1.5 (but OF COURSE in incompatible ways).

I'm not sure whether the EGL 1.5 code path (by parsing the EGL_VERSION)
is really needed, but if you ask me, it feels slightly saner not to rely
on an EGL 1.4 kludge forever. But maybe this is just an instance of
self-harm, since they will most likely never drop or not provide this
API.

Also, unlike before, we actually check the extension string for the
individual platform extensions, because who knows, some EGL
implementations might curse us if we pass unknown platform parameters.
(But actually, the more I think about this, the more bullshit it is.)

X11 and Wayland were the only ones trying to call eglGetPlatformDisplay,
so they're the only ones which are adjusted in this commit.

Unfortunately, correct function of this commit is unconfirmed. It's
possible that it crashes with the old drivers mentioned above.

Why didn't they solve it like this:

struct native_display {
    int platform_type;
    void *native_display;
};

Could have kept eglGetDisplay() without all the obnoxious extension BS.
This commit is contained in:
wm4 2019-11-30 20:48:30 +01:00
parent 4ae43a1c40
commit e1586585b4
4 changed files with 82 additions and 4 deletions

View File

@ -28,6 +28,8 @@
// Generated from presentation-time.xml // Generated from presentation-time.xml
#include "video/out/wayland/presentation-time.h" #include "video/out/wayland/presentation-time.h"
#define EGL_PLATFORM_WAYLAND_EXT 0x31D8
struct priv { struct priv {
GL gl; GL gl;
EGLDisplay egl_display; EGLDisplay egl_display;
@ -163,8 +165,9 @@ static bool egl_create_context(struct ra_ctx *ctx)
struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
struct vo_wayland_state *wl = ctx->vo->wl; struct vo_wayland_state *wl = ctx->vo->wl;
if (!(p->egl_display = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, if (!(p->egl_display = mpegl_get_display(EGL_PLATFORM_WAYLAND_EXT,
wl->display, NULL))) "EGL_EXT_platform_wayland",
wl->display)))
return false; return false;
if (eglInitialize(p->egl_display, NULL, NULL) != EGL_TRUE) if (eglInitialize(p->egl_display, NULL, NULL) != EGL_TRUE)

View File

@ -28,6 +28,8 @@
#include "oml_sync.h" #include "oml_sync.h"
#include "utils.h" #include "utils.h"
#define EGL_PLATFORM_X11_EXT 0x31D5
struct priv { struct priv {
GL gl; GL gl;
EGLDisplay egl_display; EGLDisplay egl_display;
@ -101,8 +103,9 @@ static bool mpegl_init(struct ra_ctx *ctx)
if (!vo_x11_init(vo)) if (!vo_x11_init(vo))
goto uninit; goto uninit;
p->egl_display = eglGetPlatformDisplay(EGL_PLATFORM_X11_KHR, p->egl_display = mpegl_get_display(EGL_PLATFORM_X11_EXT,
vo->x11->display, NULL); "EGL_EXT_platform_x11",
vo->x11->display);
if (!eglInitialize(p->egl_display, NULL, NULL)) { if (!eglInitialize(p->egl_display, NULL, NULL)) {
MP_MSG(ctx, msgl, "Could not initialize EGL.\n"); MP_MSG(ctx, msgl, "Could not initialize EGL.\n");
goto uninit; goto uninit;

View File

@ -277,3 +277,72 @@ void mpegl_load_functions(struct GL *gl, struct mp_log *log)
if (!gl->SwapInterval) if (!gl->SwapInterval)
gl->SwapInterval = swap_interval; gl->SwapInterval = swap_interval;
} }
// This is similar to eglGetPlatformDisplay(platform, native_display, NULL),
// except that it 1. may use eglGetPlatformDisplayEXT, 2. checks for the
// platform client extension platform_ext_name, and 3. does not support passing
// an attrib list, because the type for that parameter is different in the EXT
// and standard functions (EGL can't not fuck up, no matter what).
// platform: e.g. EGL_PLATFORM_X11_KHR
// platform_ext_name: e.g. "EGL_KHR_platform_x11"
// native_display: e.g. X11 Display*
// Returns EGL_NO_DISPLAY on failure.
// Warning: the EGL version can be different at runtime depending on the chosen
// platform, so this might return a display corresponding to some older EGL
// version (often 1.4).
// Often, there are two extension variants of a platform (KHR and EXT). If you
// need to check both, call this function twice. (Why do they define them twice?
// They're crazy.)
EGLDisplay mpegl_get_display(EGLenum platform, const char *platform_ext_name,
void *native_display)
{
// EGL is awful. Designed as ultra-portable library, it fails at dealing
// with slightly more complex environment than its short-sighted design
// could deal with. So they invented an awful, awful kludge that modifies
// EGL standard behavior, the EGL_EXT_client_extensions extension. EGL 1.4
// normally is to return NULL when querying EGL_EXTENSIONS on EGL_NO_DISPLAY,
// however, with that extension, it'll return the set of "client extensions",
// which may include EGL_EXT_platform_base.
// Prerequisite: check the platform extension.
// If this is either EGL 1.5, or 1.4 with EGL_EXT_client_extensions, then
// this must return a valid extension string.
const char *exts = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
if (!exts || !gl_check_extension(exts, platform_ext_name))
return EGL_NO_DISPLAY;
// Before we go through the EGL 1.4 BS, try if we can use native EGL 1.5
// It appears that EGL 1.4 is specified to _require_ an initialized display
// for EGL_VERSION, while EGL 1.5 is _required_ to return the EGL version.
const char *ver = eglQueryString(EGL_NO_DISPLAY, EGL_VERSION);
// Of course we have to go through the excruciating pain of parsing a
// version string, since EGL provides no other way without a display. In
// theory version!=NULL is already proof enough that it's 1.5, but be
// extra defensive, since this should have been true for EGL_EXTENSIONS as
// well, but then they added an extension that modified standard behavior.
int ma = 0, mi = 0;
if (ver && sscanf(ver, "%d.%d", &ma, &mi) == 2 && (ma > 1 || mi >= 5)) {
// This is EGL 1.5. It must support querying standard functions through
// eglGetProcAddress(). Note that on EGL 1.4, even if the function is
// unknown, it could return non-NULL anyway (because EGL is crazy).
EGLDisplay (EGLAPIENTRYP GetPlatformDisplay)
(EGLenum, void *, const EGLAttrib *) =
(void *)eglGetProcAddress("eglGetPlatformDisplay");
// (It should be impossible to be NULL, but uh.)
if (GetPlatformDisplay)
return GetPlatformDisplay(platform, native_display, NULL);
}
// (It should be impossible to be missing, but uh.)
if (!gl_check_extension(exts, "EGL_EXT_client_extensions"))
return EGL_NO_DISPLAY;
EGLDisplay (EGLAPIENTRYP GetPlatformDisplayEXT)(EGLenum, void*, const EGLint*)
= (void *)eglGetProcAddress("eglGetPlatformDisplayEXT");
// (It should be impossible to be NULL, but uh.)
if (GetPlatformDisplayEXT)
return GetPlatformDisplayEXT(platform, native_display, NULL);
return EGL_NO_DISPLAY;
}

View File

@ -29,4 +29,7 @@ bool mpegl_create_context_cb(struct ra_ctx *ctx, EGLDisplay display,
struct GL; struct GL;
void mpegl_load_functions(struct GL *gl, struct mp_log *log); void mpegl_load_functions(struct GL *gl, struct mp_log *log);
EGLDisplay mpegl_get_display(EGLenum platform, const char *platform_ext_name,
void *native_display);
#endif #endif