/**
* @file egl.c
* @brief EGL OpenGL extension module
*/
/*****************************************************************************
* Copyright © 2010-2014 Rémi Denis-Courmont
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <vlc_common.h>
#include <vlc_threads.h>
#include <vlc_plugin.h>
#include <vlc_opengl.h>
#include <vlc_window.h>
#ifdef USE_PLATFORM_X11
# include <vlc_xlib.h>
# include <X11/Xutil.h>
#endif
#ifdef USE_PLATFORM_XCB
# include <xcb/xcb.h>
# include "../xcb/events.h"
#endif
#ifdef USE_PLATFORM_WAYLAND
# include <wayland-egl.h>
#endif
#if defined (USE_PLATFORM_ANDROID)
# include "../android/utils.h"
#endif
static const char *clientExts;
#ifdef EGL_EXT_platform_base
static PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT;
static PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC createPlatformWindowSurfaceEXT;
#endif
typedef struct vlc_gl_sys_t
{
EGLDisplay display;
EGLSurface surface;
EGLContext context;
#if defined (USE_PLATFORM_X11)
Display *x11;
unsigned long x11_black_pixel;
Window x11_win;
#endif
#if defined (USE_PLATFORM_XCB)
xcb_connection_t *conn;
uint32_t xcb_black_pixel;
xcb_window_t xcb_win;
#endif
#if defined (USE_PLATFORM_WAYLAND)
struct wl_egl_window *window;
#endif
bool is_current;
} vlc_gl_sys_t;
static bool CheckAPI(EGLDisplay dpy, const char *api)
{
const char *apis = eglQueryString(dpy, EGL_CLIENT_APIS);
return vlc_gl_StrHasToken(apis, api);
}
static bool CheckClientExt(const char *name)
{
return vlc_gl_StrHasToken(clientExts, name);
}
static bool CheckDisplayExt(EGLDisplay display, const char *name)
{
const char *exts = eglQueryString(display, EGL_EXTENSIONS);
return vlc_gl_StrHasToken(exts, name);
}
static void LogDeviceInfo(vlc_gl_t *gl,
PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT,
EGLDeviceEXT device)
{
const char *device_ext = eglQueryDeviceStringEXT(device, EGL_EXTENSIONS);
if (device_ext == NULL)
device_ext = "";
/* EGL_EXT_device_query_name is a device extension, so it must be tested
* against the device's own extension string, not the client one. */
if (vlc_gl_StrHasToken(device_ext, "EGL_EXT_device_query_name"))
{
const char *vendor = eglQueryDeviceStringEXT(device, EGL_VENDOR);
const char *renderer = eglQueryDeviceStringEXT(device, EGL_RENDERER_EXT);
msg_Dbg(gl, " vendor='%s' renderer='%s'",
vendor ? vendor : "unknown", renderer ? renderer : "unknown");
}
msg_Dbg(gl, " extensions: %s", device_ext);
if (vlc_gl_StrHasToken(device_ext, "EGL_EXT_device_drm"))
{
const char *path = eglQueryDeviceStringEXT(device, EGL_DRM_DEVICE_FILE_EXT);
if (path != NULL)
msg_Dbg(gl, " drm device: %s", path);
}
if (vlc_gl_StrHasToken(device_ext, "EGL_EXT_device_drm_render_node"))
{
const char *path = eglQueryDeviceStringEXT(device, EGL_DRM_RENDER_NODE_FILE_EXT);
if (path != NULL)
msg_Dbg(gl, " drm render node: %s", path);
}
}
static void DisplayAvailableDevices(vlc_gl_t *gl)
{
PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT =
(PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress("eglQueryDevicesEXT");
PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT =
(PFNEGLQUERYDEVICESTRINGEXTPROC)eglGetProcAddress("eglQueryDeviceStringEXT");
EGLint device_count = 0;
if (eglQueryDevicesEXT(0, NULL, &device_count) != EGL_TRUE
|| device_count <= 0)
{
msg_Dbg(gl, "could not enumerate EGL devices");
return;
}
EGLDeviceEXT *devices = vlc_alloc(device_count, sizeof(*devices));
if (devices == NULL)
return;
if (eglQueryDevicesEXT(device_count, devices, &device_count) != EGL_TRUE)
{
free(devices);
return;
}
msg_Dbg(gl, "EGL has %d devices:", device_count);
for (EGLint i = 0; i < device_count; ++i)
{
msg_Dbg(gl, " - device %d:", i);
LogDeviceInfo(gl, eglQueryDeviceStringEXT, devices[i]);
}
free(devices);
}
struct gl_api
{
const char name[10];
EGLenum api;
EGLint min_minor;
EGLint render_bit;
EGLint attr[3];
};
static int MakeCurrent (vlc_gl_t *gl)
{
vlc_gl_sys_t *sys = gl->sys;
if (eglMakeCurrent (sys->display, sys->surface, sys->surface,
sys->context) != EGL_TRUE)
return VLC_EGENERIC;
sys->is_current = true;
return VLC_SUCCESS;
}
static void ReleaseCurrent (vlc_gl_t *gl)
{
vlc_gl_sys_t *sys = gl->sys;
eglMakeCurrent (sys->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
sys->is_current = false;
}
#ifdef USE_PLATFORM_WAYLAND
static void Resize (vlc_gl_t *gl, unsigned width, unsigned height)
{
vlc_gl_sys_t *sys = gl->sys;
wl_egl_window_resize(sys->window, width, height, 0, 0);
}
static void DestroySurface(vlc_gl_t *gl)
{
vlc_gl_sys_t *sys = gl->sys;
wl_egl_window_destroy(sys->window);
}
static EGLSurface CreateSurface(vlc_gl_t *gl, EGLDisplay dpy, EGLConfig config,
unsigned int width, unsigned int height)
{
vlc_gl_sys_t *sys = gl->sys;
EGLSurface surface;
sys->window = wl_egl_window_create(gl->surface->handle.wl, width, height);
if (sys->window == NULL)
return EGL_NO_SURFACE;
# if defined(EGL_VERSION_1_5)
assert(CheckClientExt("EGL_KHR_platform_wayland"));
surface = eglCreatePlatformWindowSurface(dpy, config, sys->window, NULL);
# elif defined(EGL_EXT_platform_base)
assert(CheckClientExt("EGL_EXT_platform_wayland"));
surface = createPlatformWindowSurfaceEXT(dpy, config, sys->window, NULL);
# else
surface = EGL_NO_SURFACE;
# endif
if (surface == EGL_NO_SURFACE)
wl_egl_window_destroy(sys->window);
return surface;
}
static void ReleaseDisplay(vlc_gl_t *gl)
{
(void) gl;
}
static EGLDisplay OpenDisplay(vlc_gl_t *gl)
{
vlc_window_t *surface = gl->surface;
if (surface->type != VLC_WINDOW_TYPE_WAYLAND)
return EGL_NO_DISPLAY;
if (!CheckClientExt("EGL_KHR_display_reference")) {
msg_Warn(gl, "EGL display reference counting not supported");
return EGL_NO_DISPLAY;
}
# ifdef EGL_KHR_display_reference
static const EGLAttrib attrs[] = {
EGL_TRACK_REFERENCES_KHR, EGL_TRUE,
EGL_NONE
};
# if defined(EGL_VERSION_1_5)
# ifdef EGL_KHR_platform_wayland
if (CheckClientExt("EGL_KHR_platform_wayland"))
return eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR,
surface->display.wl, attrs);
# endif
# elif defined(EGL_EXT_platform_wayland)
if (CheckClientExt("EGL_EXT_platform_wayland"))
return getPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT,
surface->display.wl, attrs);
# endif
# endif
return EGL_NO_DISPLAY;
}
#elif defined(USE_PLATFORM_X11)
static void Resize (vlc_gl_t *gl, unsigned width, unsigned height)
{
vlc_gl_sys_t *sys = gl->sys;
EGLint val;
EGLDisplay old_display = eglGetCurrentDisplay();
EGLSurface old_draw = eglGetCurrentSurface(EGL_DRAW);
EGLSurface old_read = eglGetCurrentSurface(EGL_READ);
EGLContext old_ctx = eglGetCurrentContext();
bool made_current = eglMakeCurrent(sys->display, sys->surface, sys->surface, sys->context);
if (made_current)
eglWaitClient();
unsigned long init_serial = LastKnownRequestProcessed(sys->x11);
unsigned long resize_serial = NextRequest(sys->x11);
XResizeWindow(sys->x11, sys->x11_win, width, height);
if (made_current)
{
eglQuerySurface(sys->display, sys->surface, EGL_HEIGHT, &val); /* force Mesa to see new size in time for next draw */
eglWaitNative(EGL_CORE_NATIVE_ENGINE);
if (old_display != EGL_NO_DISPLAY)
eglMakeCurrent(old_display, old_draw, old_read, old_ctx);
else
eglMakeCurrent(sys->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
if (LastKnownRequestProcessed(sys->x11) - init_serial < resize_serial - init_serial)
XSync(sys->x11, False);
}
static void DestroySurface(vlc_gl_t *gl)
{
vlc_gl_sys_t *sys = gl->sys;
XUnmapWindow(sys->x11, sys->x11_win);
}
static EGLSurface CreateSurface(vlc_gl_t *gl, EGLDisplay dpy, EGLConfig config,
unsigned int width, unsigned int height)
{
vlc_gl_sys_t *sys = gl->sys;
EGLint val;
if (eglGetConfigAttrib(dpy, config, EGL_NATIVE_VISUAL_ID, &val) == EGL_FALSE)
return EGL_NO_SURFACE;
XVisualInfo vinfo_template;
int vinfoc;
vinfo_template.visualid = (VisualID) val;
XVisualInfo *vinfov = XGetVisualInfo(sys->x11, VisualIDMask, &vinfo_template, &vinfoc);
if (vinfov == NULL || vinfoc == 0)
return EGL_NO_SURFACE;
Visual *visual = vinfov[0].visual;
int depth = vinfov[0].depth;
XFree(vinfov);
unsigned long mask =
CWBackPixel | CWBorderPixel | CWBitGravity | CWColormap;
XSetWindowAttributes swa = {
.background_pixel = sys->x11_black_pixel,
.border_pixel = sys->x11_black_pixel,
.bit_gravity = NorthWestGravity,
.colormap = XCreateColormap(sys->x11, gl->surface->handle.xid, visual,
AllocNone),
};
Window win = XCreateWindow(sys->x11, gl->surface->handle.xid, 0, 0, width,
height, 0, depth, InputOutput, visual, mask,
&swa);
sys->x11_win = win;
XMapWindow(sys->x11, win);
# if defined(EGL_VERSION_1_5)
if (CheckClientExt("EGL_KHR_platform_x11"))
return eglCreatePlatformWindowSurface(dpy, config, &win, NULL);
# elif defined(EGL_EXT_platform_base)
if (CheckClientExt("EGL_EXT_platform_x11"))
return createPlatformWindowSurfaceEXT(dpy, config, &win, NULL);
# endif
return eglCreateWindowSurface(dpy, config, win, NULL);
}
static void ReleaseDisplay(vlc_gl_t *gl)
{
vlc_gl_sys_t *sys = gl->sys;
XCloseDisplay(sys->x11);
}
static EGLDisplay OpenDisplay(vlc_gl_t *gl)
{
vlc_window_t *surface = gl->surface;
vlc_gl_sys_t *sys = gl->sys;
if (surface->type != VLC_WINDOW_TYPE_XID || !vlc_xlib_init(VLC_OBJECT(gl)))
return EGL_NO_DISPLAY;
Display *x11 = XOpenDisplay(surface->display.x11);
if (x11 == NULL)
return EGL_NO_DISPLAY;
XWindowAttributes wa;
if (!XGetWindowAttributes(x11, surface->handle.xid, &wa))
goto error;
EGLDisplay display;
int snum = XScreenNumberOfScreen(wa.screen);
# if defined(EGL_VERSION_1_5)
# ifdef EGL_KHR_paltform_x11
if (CheckClientExt("EGL_KHR_platform_x11")) {
const EGLAttrib attrs[] = {
EGL_PLATFORM_X11_SCREEN_KHR, snum,
EGL_NONE
};
display = getPlatformDisplayEXT(EGL_PLATFORM_X11_KHR, x11, attrs);
} else
# endif
# elif defined(EGL_EXT_platform_x11)
if (CheckClientExt("EGL_EXT_platform_x11")) {
const EGLint attrs[] = {
EGL_PLATFORM_X11_SCREEN_EXT, snum,
EGL_NONE
};
display = getPlatformDisplayEXT(EGL_PLATFORM_X11_EXT, x11, attrs);
} else
# endif
# ifdef __unix__
if (snum == XDefaultScreen(x11))
display = eglGetDisplay(x11);
else
# endif
display = EGL_NO_DISPLAY;
if (display == EGL_NO_DISPLAY)
goto error;
sys->x11 = x11;
sys->x11_black_pixel = BlackPixelOfScreen(wa.screen);
return display;
error:
XCloseDisplay(x11);
return EGL_NO_DISPLAY;
}
#elif defined(USE_PLATFORM_XCB)
static int GetScreenNum(xcb_connection_t *conn, const xcb_screen_t *scr)
{
xcb_screen_iterator_t i = xcb_setup_roots_iterator(xcb_get_setup(conn));
int n = 0;
while (scr != i.data) {
xcb_screen_next(&i);
n++;
}
return n;
}
static void Resize (vlc_gl_t *gl, unsigned width, unsigned height)
{
vlc_gl_sys_t *sys = gl->sys;
EGLint val;
EGLDisplay old_display = eglGetCurrentDisplay();
EGLSurface old_draw = eglGetCurrentSurface(EGL_DRAW);
EGLSurface old_read = eglGetCurrentSurface(EGL_READ);
EGLContext old_ctx = eglGetCurrentContext();
bool made_current = eglMakeCurrent(sys->display, sys->surface, sys->surface, sys->context);
if (made_current)
eglWaitClient();
uint16_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
const uint32_t values[] = { width, height };
xcb_void_cookie_t cookie = xcb_configure_window_checked(sys->conn, sys->xcb_win, mask, values);
if (made_current)
{
eglQuerySurface(sys->display, sys->surface, EGL_HEIGHT, &val); /* force Mesa to see new size in time for next draw */
eglWaitNative(EGL_CORE_NATIVE_ENGINE);
if (old_display != EGL_NO_DISPLAY)
eglMakeCurrent(old_display, old_draw, old_read, old_ctx);
else
eglMakeCurrent(sys->display, NULL, NULL, NULL);
}
free(xcb_request_check(sys->conn, cookie));
}
static void DestroySurface(vlc_gl_t *gl)
{
vlc_gl_sys_t *sys = gl->sys;
xcb_unmap_window(sys->conn, sys->xcb_win);
}
static uint8_t VisualIdToDepth(xcb_connection_t *conn, xcb_visualid_t vid)
{
xcb_screen_iterator_t si = xcb_setup_roots_iterator (xcb_get_setup (conn));
for (; si.rem; xcb_screen_next (&si)) {
xcb_depth_iterator_t di = xcb_screen_allowed_depths_iterator (si.data);
for (; di.rem; xcb_depth_next (&di)) {
xcb_visualtype_iterator_t vi = xcb_depth_visuals_iterator (di.data);
for (; vi.rem; xcb_visualtype_next (&vi)) {
if (vid == vi.data->visual_id) {
return di.data->depth;
}
}
}
}
return 0;
}
static EGLSurface CreateSurface(vlc_gl_t *gl, EGLDisplay dpy, EGLConfig config,
unsigned int width, unsigned int height)
{
# ifdef EGL_EXT_platform_base
vlc_gl_sys_t *sys = gl->sys;
xcb_connection_t *conn = sys->conn;
EGLint val;
if (eglGetConfigAttrib(dpy, config, EGL_NATIVE_VISUAL_ID, &val) == EGL_FALSE)
return EGL_NO_SURFACE;
xcb_visualid_t vid = (xcb_visualid_t) val;
xcb_colormap_t cmap = xcb_generate_id(conn);
xcb_create_colormap(conn, XCB_COLORMAP_ALLOC_NONE,
cmap, gl->surface->handle.xid, vid);
uint8_t depth = VisualIdToDepth(conn, vid);
if (depth == 0)
return EGL_NO_SURFACE;
xcb_window_t win = xcb_generate_id(conn);
uint32_t mask =
XCB_CW_BACK_PIXEL |
XCB_CW_BORDER_PIXEL |
XCB_CW_BIT_GRAVITY |
XCB_CW_COLORMAP;
const uint32_t values[] = {
/* XCB_CW_BACK_PIXEL */
sys->xcb_black_pixel,
/* XCB_CW_BORDER_PIXEL */
sys->xcb_black_pixel,
/* XCB_CW_BIT_GRAVITY */
XCB_GRAVITY_NORTH_WEST,
/* XCB_CW_COLORMAP */
cmap,
};
xcb_create_window(conn, depth, win, gl->surface->handle.xid, 0, 0,
width, height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, vid,
mask, values);
xcb_map_window(conn, win);
sys->xcb_win = win;
assert(CheckClientExt("EGL_EXT_platform_xcb"));
return createPlatformWindowSurfaceEXT(dpy, config, &win, NULL);
# else
return EGL_NO_SURFACE;
# endif
}
static void ReleaseDisplay(vlc_gl_t *gl)
{
vlc_gl_sys_t *sys = gl->sys;
xcb_disconnect(sys->conn);
}
static EGLDisplay OpenDisplay(vlc_gl_t *gl)
{
# ifdef EGL_EXT_platform_xcb
vlc_window_t *surface = gl->surface;
vlc_gl_sys_t *sys = gl->sys;
xcb_connection_t *conn;
const xcb_screen_t *scr;
if (surface->type != VLC_WINDOW_TYPE_XID)
return EGL_NO_DISPLAY;
if (!CheckClientExt("EGL_EXT_platform_xcb"))
return EGL_NO_DISPLAY;
if (vlc_xcb_parent_Create(gl->obj.logger, surface, &conn,
&scr) != VLC_SUCCESS)
return EGL_NO_DISPLAY;
const EGLint attrs[] = {
EGL_PLATFORM_XCB_SCREEN_EXT, GetScreenNum(conn, scr),
EGL_NONE
};
EGLDisplay display = getPlatformDisplayEXT(EGL_PLATFORM_XCB_EXT, conn,
attrs);
if (display == EGL_NO_DISPLAY) {
xcb_disconnect(conn);
goto out;
}
sys->conn = conn;
sys->xcb_black_pixel = scr->black_pixel;
out:
return display;
# else
return EGL_NO_DISPLAY;
# endif
}
#elif defined (USE_PLATFORM_ANDROID)
# define Resize (NULL)
static void DestroySurface(vlc_gl_t *gl)
{
AWindowHandler_releaseANativeWindow(gl->surface->display.anativewindow,
gl->surface->handle.android_id);
}
static EGLSurface CreateSurface(vlc_gl_t *gl, EGLDisplay dpy, EGLConfig config,
unsigned int width, unsigned int height)
{
ANativeWindow *anw =
AWindowHandler_getANativeWindow(gl->surface->display.anativewindow,
gl->surface->handle.android_id);
if (anw == NULL)
return EGL_NO_SURFACE;
int awh_caps = AWindowHandler_getCapabilities(gl->surface->display.anativewindow);
if ((awh_caps & AWH_CAPS_SURFACE_VIEW) == 0)
{
ANativeWindow_setBuffersGeometry(anw, width, height, AHARDWAREBUFFER_FORMAT_BLOB);
}
return eglCreateWindowSurface(dpy, config, anw, NULL);
}
static void ReleaseDisplay(vlc_gl_t *gl)
{
(void) gl;
}
static EGLDisplay OpenDisplay(vlc_gl_t *gl)
{
# if defined (__ANDROID__) || defined (ANDROID)
if (gl->surface->type == VLC_WINDOW_TYPE_ANDROID_NATIVE)
return eglGetDisplay(EGL_DEFAULT_DISPLAY);
# endif
return EGL_NO_DISPLAY;
}
#else
# define Resize (NULL)
static void DestroySurface(vlc_gl_t *gl)
{
(void) gl;
}
static EGLSurface CreateSurface(vlc_gl_t *gl, EGLDisplay dpy, EGLConfig config,
unsigned int width, unsigned int height)
{
HWND window = gl->surface->handle.hwnd;
(void) width; (void) height;
return eglCreateWindowSurface(dpy, config, window, NULL);
}
static void ReleaseDisplay(vlc_gl_t *gl)
{
(void) gl;
}
static EGLDisplay OpenDisplay(vlc_gl_t *gl)
{
# if defined (_WIN32) || defined (__VC32__) \
&& !defined (__CYGWIN__) && !defined (__SCITECH_SNAP__)
if (gl->surface->type == VLC_WINDOW_TYPE_HWND)
return eglGetDisplay(EGL_DEFAULT_DISPLAY);
# endif
return EGL_NO_DISPLAY;
}
#endif
static void SwapBuffers (vlc_gl_t *gl)
{
vlc_gl_sys_t *sys = gl->sys;
if (!sys->is_current)
{
EGLDisplay previous_display = eglGetCurrentDisplay();
EGLSurface s_read = eglGetCurrentSurface(EGL_READ);
EGLSurface s_draw = eglGetCurrentSurface(EGL_DRAW);
EGLContext previous_context = eglGetCurrentContext();
eglMakeCurrent(sys->display, sys->surface, sys->surface, sys->context);
eglSwapBuffers (sys->display, sys->surface);
if (previous_display != EGL_NO_DISPLAY)
eglMakeCurrent(previous_display, s_draw, s_read, previous_context);
else
eglMakeCurrent(sys->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
else
eglSwapBuffers (sys->display, sys->surface);
}
static void *GetSymbol(vlc_gl_t *gl, const char *procname)
{
(void) gl;
return (void *)eglGetProcAddress (procname);
}
static void Close(vlc_gl_t *gl)
{
vlc_gl_sys_t *sys = gl->sys;
if (sys->context != EGL_NO_CONTEXT)
eglDestroyContext(sys->display, sys->context);
if (sys->surface != EGL_NO_SURFACE) {
eglDestroySurface(sys->display, sys->surface);
DestroySurface(gl);
}
eglTerminate(sys->display);
ReleaseDisplay(gl);
free (sys);
}
static void InitEGL(void)
{
static vlc_once_t once = VLC_STATIC_ONCE;
if (unlikely(!vlc_once_begin(&once))) {
clientExts = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
if (!clientExts)
clientExts = "";
#ifdef EGL_EXT_platform_base
getPlatformDisplayEXT =
(void *) eglGetProcAddress("eglGetPlatformDisplayEXT");
createPlatformWindowSurfaceEXT =
(void *) eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
#endif
vlc_once_complete(&once);
}
}
/**
* Probe EGL display availability
*/
static int Open(vlc_gl_t *gl, const struct gl_api *api,
unsigned width, unsigned height,
const struct vlc_gl_cfg *gl_cfg)
{
InitEGL();
int ret = VLC_EGENERIC;
vlc_object_t *obj = VLC_OBJECT(gl);
vlc_gl_sys_t *sys = malloc(sizeof (*sys));
if (unlikely(sys == NULL))
return VLC_ENOMEM;
msg_Dbg(obj, "EGL client extensions: %s", clientExts);
gl->sys = sys;
sys->display = EGL_NO_DISPLAY;
sys->surface = EGL_NO_SURFACE;
sys->context = EGL_NO_CONTEXT;
sys->is_current = false;
sys->display = OpenDisplay(gl);
if (sys->display == EGL_NO_DISPLAY) {
free(sys);
return VLC_ENOTSUP;
}
/* Initialize EGL display */
EGLint major, minor;
if (eglInitialize(sys->display, &major, &minor) != EGL_TRUE)
goto error;
msg_Dbg(obj, "EGL version %s by %s",
eglQueryString(sys->display, EGL_VERSION),
eglQueryString(sys->display, EGL_VENDOR));
const char *ext = eglQueryString(sys->display, EGL_EXTENSIONS);
if (*ext)
msg_Dbg(obj, "EGL display extensions: %s", ext);
if (major != 1 || minor < api->min_minor
|| !CheckAPI(sys->display, api->name))
{
msg_Err(obj, "cannot select %s API", api->name);
goto error;
}
bool has_device_enum = CheckClientExt("EGL_EXT_device_enumeration");
bool has_device_query = CheckClientExt("EGL_EXT_device_query");
if (has_device_query && has_device_enum)
{
DisplayAvailableDevices(gl);
PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT =
(PFNEGLQUERYDISPLAYATTRIBEXTPROC)eglGetProcAddress("eglQueryDisplayAttribEXT");
PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT =
(PFNEGLQUERYDEVICESTRINGEXTPROC)eglGetProcAddress("eglQueryDeviceStringEXT");
EGLAttrib device_attr;
if (eglQueryDisplayAttribEXT(sys->display, EGL_DEVICE_EXT,
&device_attr) == EGL_TRUE)
{
EGLDeviceEXT device = (EGLDeviceEXT)device_attr;
msg_Dbg(gl, "Using EGL device:");
LogDeviceInfo(gl, eglQueryDeviceStringEXT, device);
}
else
msg_Dbg(gl, "could not query the EGL device in use");
}
bool has_query_driver =
CheckDisplayExt(sys->display, "EGL_MESA_query_driver");
if (has_query_driver)
{
PFNEGLGETDISPLAYDRIVERNAMEPROC eglGetDisplayDriverName =
(PFNEGLGETDISPLAYDRIVERNAMEPROC) eglGetProcAddress("eglGetDisplayDriverName");
const char *driver = eglGetDisplayDriverName(sys->display);
msg_Dbg(gl, "Using EGL driver: %s", driver ? driver : "unknown");
}
else
msg_Dbg(gl, "EGL_MESA_query_driver is unavailable");
const EGLint conf_attr[] = {
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 5,
EGL_BLUE_SIZE, 5,
EGL_ALPHA_SIZE, gl_cfg->need_alpha ? 5 : 0,
EGL_RENDERABLE_TYPE, api->render_bit,
EGL_NONE
};
EGLConfig cfgv[1];
EGLint cfgc;
if (eglChooseConfig(sys->display, conf_attr, cfgv, 1, &cfgc) != EGL_TRUE
|| cfgc == 0)
{
msg_Err (obj, "cannot choose EGL configuration");
goto error;
}
/* Create a drawing surface */
sys->surface = CreateSurface(gl, sys->display, cfgv[0], width, height);
if (sys->surface == EGL_NO_SURFACE)
{
msg_Err (obj, "cannot create EGL window surface");
goto error;
}
if (eglBindAPI (api->api) != EGL_TRUE)
{
msg_Err (obj, "cannot bind EGL API");
goto error;
}
EGLContext ctx = eglCreateContext(sys->display, cfgv[0], EGL_NO_CONTEXT,
api->attr);
if (ctx == EGL_NO_CONTEXT)
{
msg_Err (obj, "cannot create EGL context");
goto error;
}
sys->context = ctx;
/* Initialize OpenGL callbacks */
static const struct vlc_gl_operations ops =
{
.make_current = MakeCurrent,
.release_current = ReleaseCurrent,
.resize = Resize,
.swap = SwapBuffers,
.get_proc_address = GetSymbol,
.close = Close,
};
gl->ops = &ops;
return VLC_SUCCESS;
error:
Close(gl);
return ret;
}
static int OpenGLES2(vlc_gl_t *gl, unsigned width, unsigned height,
const struct vlc_gl_cfg *gl_cfg)
{
static const struct gl_api api = {
"OpenGL_ES", EGL_OPENGL_ES_API, 4, EGL_OPENGL_ES2_BIT,
{ EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE },
};
return Open(gl, &api, width, height, gl_cfg);
}
static int OpenGL(vlc_gl_t *gl, unsigned width, unsigned height,
const struct vlc_gl_cfg *gl_cfg)
{
static const struct gl_api api = {
"OpenGL", EGL_OPENGL_API, 4, EGL_OPENGL_BIT,
{ EGL_NONE },
};
return Open(gl, &api, width, height, gl_cfg);
}
#ifdef USE_PLATFORM_XCB
# define VLC_PRIORITY 60
#endif
#ifndef VLC_PRIORITY
# define VLC_PRIORITY 50
#endif
vlc_module_begin ()
set_shortname (N_("EGL"))
set_description (N_("EGL extension for OpenGL"))
set_subcategory (SUBCAT_VIDEO_VOUT)
set_callback_opengl(OpenGL, VLC_PRIORITY)
add_shortcut ("egl")
add_submodule ()
set_callback_opengl_es2(OpenGLES2, VLC_PRIORITY)
add_shortcut ("egl")
vlc_module_end ()
¤ Dauer der Verarbeitung: 0.11 Sekunden
¤
*© Formatika GbR, Deutschland
|
|