2015-12-19 11:45:07 +00:00
|
|
|
/*
|
|
|
|
* This file is part of mpv.
|
|
|
|
*
|
2016-01-19 17:36:34 +00:00
|
|
|
* mpv 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.
|
2015-12-19 11:45:07 +00:00
|
|
|
*
|
|
|
|
* mpv 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
|
2016-01-19 17:36:34 +00:00
|
|
|
* GNU Lesser General Public License for more details.
|
2015-12-19 11:45:07 +00:00
|
|
|
*
|
2016-01-19 17:36:34 +00:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
|
2015-12-19 11:45:07 +00:00
|
|
|
*/
|
|
|
|
|
2017-04-06 12:50:19 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#if HAVE_LIBDL
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#endif
|
|
|
|
|
2016-09-13 13:38:51 +00:00
|
|
|
#include "common/common.h"
|
|
|
|
|
2015-12-19 11:45:07 +00:00
|
|
|
#include "egl_helpers.h"
|
|
|
|
#include "common.h"
|
2016-09-13 13:38:51 +00:00
|
|
|
#include "context.h"
|
|
|
|
|
2017-02-07 14:27:17 +00:00
|
|
|
#if HAVE_EGL_ANGLE
|
2017-02-04 08:16:02 +00:00
|
|
|
// On Windows, egl_helpers.c is only used by ANGLE, where the EGL functions may
|
|
|
|
// be loaded dynamically from ANGLE DLLs
|
|
|
|
#include "angle_dynamic.h"
|
|
|
|
#endif
|
|
|
|
|
2016-09-13 13:38:51 +00:00
|
|
|
// EGL 1.5
|
|
|
|
#ifndef EGL_CONTEXT_OPENGL_PROFILE_MASK
|
2016-09-14 08:05:00 +00:00
|
|
|
#define EGL_CONTEXT_MAJOR_VERSION 0x3098
|
|
|
|
#define EGL_CONTEXT_MINOR_VERSION 0x30FB
|
2016-09-13 13:38:51 +00:00
|
|
|
#define EGL_CONTEXT_OPENGL_PROFILE_MASK 0x30FD
|
|
|
|
#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT 0x00000001
|
2016-09-14 08:05:00 +00:00
|
|
|
#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE 0x31B1
|
|
|
|
#define EGL_OPENGL_ES3_BIT 0x00000040
|
2016-09-13 13:38:51 +00:00
|
|
|
#endif
|
|
|
|
|
2016-12-30 18:24:34 +00:00
|
|
|
// es_version = 0 (desktop), 2/3 (ES major version)
|
2016-09-30 10:20:35 +00:00
|
|
|
static bool create_context(EGLDisplay display, struct mp_log *log, bool probing,
|
2016-12-30 18:24:34 +00:00
|
|
|
int es_version, struct mpegl_opts *opts,
|
2016-09-13 13:38:51 +00:00
|
|
|
EGLContext *out_context, EGLConfig *out_config)
|
|
|
|
{
|
2016-09-30 10:20:35 +00:00
|
|
|
int msgl = probing ? MSGL_V : MSGL_FATAL;
|
2016-09-13 13:38:51 +00:00
|
|
|
|
2016-09-14 08:05:00 +00:00
|
|
|
EGLenum api = EGL_OPENGL_API;
|
|
|
|
EGLint rend = EGL_OPENGL_BIT;
|
|
|
|
const char *name = "Desktop OpenGL";
|
2016-12-30 18:24:34 +00:00
|
|
|
if (es_version == 2) {
|
2016-09-14 08:05:00 +00:00
|
|
|
api = EGL_OPENGL_ES_API;
|
|
|
|
rend = EGL_OPENGL_ES2_BIT;
|
|
|
|
name = "GLES 2.0";
|
|
|
|
}
|
2016-12-30 18:24:34 +00:00
|
|
|
if (es_version == 3) {
|
2016-09-14 08:05:00 +00:00
|
|
|
api = EGL_OPENGL_ES_API;
|
|
|
|
rend = EGL_OPENGL_ES3_BIT;
|
|
|
|
name = "GLES 3.x";
|
|
|
|
}
|
|
|
|
|
|
|
|
mp_msg(log, MSGL_V, "Trying to create %s context.\n", name);
|
|
|
|
|
|
|
|
if (!eglBindAPI(api)) {
|
2016-09-13 18:38:05 +00:00
|
|
|
mp_msg(log, MSGL_V, "Could not bind API!\n");
|
2016-09-13 13:38:51 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-09-14 08:05:00 +00:00
|
|
|
|
2016-09-13 13:38:51 +00:00
|
|
|
EGLint attributes[] = {
|
|
|
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
|
|
|
EGL_RED_SIZE, 1,
|
|
|
|
EGL_GREEN_SIZE, 1,
|
|
|
|
EGL_BLUE_SIZE, 1,
|
2016-12-30 18:24:34 +00:00
|
|
|
EGL_ALPHA_SIZE, (opts->vo_flags & VOFLAG_ALPHA ) ? 1 : 0,
|
2016-09-14 08:05:00 +00:00
|
|
|
EGL_RENDERABLE_TYPE, rend,
|
2016-09-13 13:38:51 +00:00
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
|
2016-12-31 12:55:44 +00:00
|
|
|
EGLint num_configs;
|
|
|
|
if (!eglChooseConfig(display, attributes, NULL, 0, &num_configs))
|
|
|
|
num_configs = 0;
|
2016-09-13 13:38:51 +00:00
|
|
|
|
2016-12-31 12:55:44 +00:00
|
|
|
EGLConfig *configs = talloc_array(NULL, EGLConfig, num_configs);
|
|
|
|
if (!eglChooseConfig(display, attributes, configs, num_configs, &num_configs))
|
|
|
|
num_configs = 0;
|
2016-12-30 18:52:47 +00:00
|
|
|
|
2016-12-31 12:55:44 +00:00
|
|
|
if (!num_configs) {
|
|
|
|
talloc_free(configs);
|
|
|
|
mp_msg(log, msgl, "Could not choose EGLConfig!\n");
|
2016-09-13 13:38:51 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-12-30 18:52:47 +00:00
|
|
|
int chosen = 0;
|
|
|
|
if (opts->refine_config)
|
2016-12-31 12:55:44 +00:00
|
|
|
chosen = opts->refine_config(opts->user_data, configs, num_configs);
|
2016-12-30 18:52:47 +00:00
|
|
|
EGLConfig config = configs[chosen];
|
|
|
|
|
|
|
|
talloc_free(configs);
|
|
|
|
|
2016-09-14 08:05:00 +00:00
|
|
|
EGLContext *ctx = NULL;
|
|
|
|
|
2016-12-30 18:24:34 +00:00
|
|
|
if (es_version) {
|
2016-09-14 08:05:00 +00:00
|
|
|
EGLint attrs[] = {
|
2016-12-30 18:24:34 +00:00
|
|
|
EGL_CONTEXT_CLIENT_VERSION, es_version,
|
2016-09-14 08:05:00 +00:00
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
|
|
|
|
ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
|
|
|
|
} else {
|
|
|
|
for (int n = 0; mpgl_preferred_gl_versions[n]; n++) {
|
|
|
|
int ver = mpgl_preferred_gl_versions[n];
|
|
|
|
|
|
|
|
EGLint attrs[] = {
|
|
|
|
EGL_CONTEXT_MAJOR_VERSION, MPGL_VER_GET_MAJOR(ver),
|
|
|
|
EGL_CONTEXT_MINOR_VERSION, MPGL_VER_GET_MINOR(ver),
|
|
|
|
EGL_CONTEXT_OPENGL_PROFILE_MASK,
|
|
|
|
ver >= 320 ? EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT : 0,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
|
|
|
|
ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
|
|
|
|
if (ctx)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ctx) {
|
|
|
|
// Fallback for EGL 1.4 without EGL_KHR_create_context.
|
|
|
|
EGLint attrs[] = { EGL_NONE };
|
|
|
|
|
|
|
|
ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs);
|
|
|
|
}
|
2016-09-13 13:38:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!ctx) {
|
|
|
|
mp_msg(log, msgl, "Could not create EGL context!\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
*out_context = ctx;
|
|
|
|
*out_config = config;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-09-14 08:38:37 +00:00
|
|
|
#define STR_OR_ERR(s) ((s) ? (s) : "(error)")
|
|
|
|
|
2016-09-13 13:38:51 +00:00
|
|
|
// Create a context and return it and the config it was created with. If it
|
|
|
|
// returns false, the out_* pointers are set to NULL.
|
|
|
|
// vo_flags is a combination of VOFLAG_* values.
|
|
|
|
bool mpegl_create_context(EGLDisplay display, struct mp_log *log, int vo_flags,
|
|
|
|
EGLContext *out_context, EGLConfig *out_config)
|
|
|
|
{
|
2016-12-30 18:24:34 +00:00
|
|
|
return mpegl_create_context_opts(display, log,
|
|
|
|
&(struct mpegl_opts){.vo_flags = vo_flags}, out_context, out_config);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a context and return it and the config it was created with. If it
|
|
|
|
// returns false, the out_* pointers are set to NULL.
|
|
|
|
bool mpegl_create_context_opts(EGLDisplay display, struct mp_log *log,
|
|
|
|
struct mpegl_opts *opts,
|
|
|
|
EGLContext *out_context, EGLConfig *out_config)
|
|
|
|
{
|
|
|
|
assert(opts);
|
|
|
|
|
2016-09-13 13:38:51 +00:00
|
|
|
*out_context = NULL;
|
|
|
|
*out_config = NULL;
|
|
|
|
|
2016-09-14 08:38:37 +00:00
|
|
|
const char *version = eglQueryString(display, EGL_VERSION);
|
|
|
|
const char *vendor = eglQueryString(display, EGL_VENDOR);
|
|
|
|
const char *apis = eglQueryString(display, EGL_CLIENT_APIS);
|
|
|
|
mp_verbose(log, "EGL_VERSION=%s\nEGL_VENDOR=%s\nEGL_CLIENT_APIS=%s\n",
|
|
|
|
STR_OR_ERR(version), STR_OR_ERR(vendor), STR_OR_ERR(apis));
|
|
|
|
|
2016-12-30 18:24:34 +00:00
|
|
|
bool probing = opts->vo_flags & VOFLAG_PROBING;
|
2016-09-30 10:20:35 +00:00
|
|
|
int msgl = probing ? MSGL_V : MSGL_FATAL;
|
2017-01-26 10:33:58 +00:00
|
|
|
bool try_gles = !(opts->vo_flags & VOFLAG_NO_GLES);
|
2016-09-13 13:38:51 +00:00
|
|
|
|
2016-12-30 18:24:34 +00:00
|
|
|
if (!(opts->vo_flags & VOFLAG_GLES)) {
|
2016-09-14 08:05:00 +00:00
|
|
|
// Desktop OpenGL
|
2017-01-26 10:33:58 +00:00
|
|
|
if (create_context(display, log, try_gles | probing, 0, opts,
|
2016-09-13 13:38:51 +00:00
|
|
|
out_context, out_config))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-20 03:57:51 +00:00
|
|
|
if (try_gles && !(opts->vo_flags & VOFLAG_GLES2)) {
|
2016-09-14 08:05:00 +00:00
|
|
|
// ES 3.x
|
2016-12-30 18:24:34 +00:00
|
|
|
if (create_context(display, log, true, 3, opts,
|
2016-09-14 08:05:00 +00:00
|
|
|
out_context, out_config))
|
|
|
|
return true;
|
2017-03-20 03:57:51 +00:00
|
|
|
}
|
2016-09-14 08:05:00 +00:00
|
|
|
|
2017-03-20 03:57:51 +00:00
|
|
|
if (try_gles) {
|
2016-09-14 08:05:00 +00:00
|
|
|
// ES 2.0
|
2016-12-30 18:24:34 +00:00
|
|
|
if (create_context(display, log, probing, 2, opts,
|
2016-09-13 13:38:51 +00:00
|
|
|
out_context, out_config))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
mp_msg(log, msgl, "Could not create a GL context.\n");
|
|
|
|
return false;
|
|
|
|
}
|
2015-12-19 11:45:07 +00:00
|
|
|
|
2017-04-06 12:50:19 +00:00
|
|
|
static void *mpegl_get_proc_address(void *ctx, const char *name)
|
|
|
|
{
|
|
|
|
void *p = eglGetProcAddress(name);
|
|
|
|
#if defined(__GLIBC__) && HAVE_LIBDL
|
|
|
|
// Some crappy ARM/Linux things do not provide EGL 1.5, so above call does
|
|
|
|
// not necessarily return function pointers for core functions. Try to get
|
|
|
|
// them from a loaded GLES lib. As POSIX leaves RTLD_DEFAULT "reserved",
|
|
|
|
// use it only with glibc.
|
|
|
|
if (!p)
|
|
|
|
p = dlsym(RTLD_DEFAULT, name);
|
|
|
|
#endif
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load gl version and function pointers into *gl.
|
|
|
|
// Expects a current EGL context set.
|
|
|
|
void mpegl_load_functions(struct GL *gl, struct mp_log *log)
|
|
|
|
{
|
|
|
|
const char *egl_exts = "";
|
|
|
|
EGLDisplay display = eglGetCurrentDisplay();
|
|
|
|
if (display != EGL_NO_DISPLAY)
|
|
|
|
egl_exts = eglQueryString(display, EGL_EXTENSIONS);
|
|
|
|
|
|
|
|
mpgl_load_functions2(gl, mpegl_get_proc_address, NULL, egl_exts, log);
|
|
|
|
}
|