1 /*
2 * This file is part of libplacebo.
3 *
4 * libplacebo is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * libplacebo is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "common.h"
19 #include "utils.h"
20 #include "gpu.h"
21 #include "pl_thread.h"
22
23 const struct pl_opengl_params pl_opengl_default_params = {0};
24
25 struct priv {
26 struct pl_opengl_params params;
27 pl_log log;
28 bool is_debug;
29 bool is_debug_egl;
30
31 // For context locking
32 pl_mutex lock;
33 int count;
34 };
35
debug_cb(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar * message,const void * userParam)36 static void GLAPIENTRY debug_cb(GLenum source, GLenum type, GLuint id,
37 GLenum severity, GLsizei length,
38 const GLchar *message, const void *userParam)
39 {
40 pl_log log = (void *) userParam;
41 enum pl_log_level level = PL_LOG_ERR;
42
43 switch (severity) {
44 case GL_DEBUG_SEVERITY_NOTIFICATION:level = PL_LOG_DEBUG; break;
45 case GL_DEBUG_SEVERITY_LOW: level = PL_LOG_INFO; break;
46 case GL_DEBUG_SEVERITY_MEDIUM: level = PL_LOG_WARN; break;
47 case GL_DEBUG_SEVERITY_HIGH: level = PL_LOG_ERR; break;
48 }
49
50 pl_msg(log, level, "GL: %s", message);
51 }
52
53 #ifdef EPOXY_HAS_EGL
54
debug_cb_egl(EGLenum error,const char * command,EGLint messageType,EGLLabelKHR threadLabel,EGLLabelKHR objectLabel,const char * message)55 static void debug_cb_egl(EGLenum error, const char *command,
56 EGLint messageType, EGLLabelKHR threadLabel,
57 EGLLabelKHR objectLabel, const char *message)
58 {
59 pl_log log = threadLabel;
60 enum pl_log_level level = PL_LOG_ERR;
61
62 switch (messageType) {
63 case EGL_DEBUG_MSG_CRITICAL_KHR: level = PL_LOG_FATAL; break;
64 case EGL_DEBUG_MSG_ERROR_KHR: level = PL_LOG_ERR; break;
65 case EGL_DEBUG_MSG_WARN_KHR: level = PL_LOG_WARN; break;
66 case EGL_DEBUG_MSG_INFO_KHR: level = PL_LOG_DEBUG; break;
67 }
68
69 pl_msg(log, level, "EGL: %s: %s %s", command, egl_err_str(error),
70 message);
71 }
72
73 #endif // EPOXY_HAS_EGL
74
pl_opengl_destroy(pl_opengl * ptr)75 void pl_opengl_destroy(pl_opengl *ptr)
76 {
77 pl_opengl pl_gl = *ptr;
78 if (!pl_gl)
79 return;
80
81 struct priv *p = PL_PRIV(pl_gl);
82 if (!gl_make_current(pl_gl)) {
83 PL_WARN(p, "Failed uninitializing OpenGL context, leaking resources!");
84 return;
85 }
86
87 if (p->is_debug)
88 glDebugMessageCallback(NULL, NULL);
89
90 #ifdef EPOXY_HAS_EGL
91 if (p->is_debug_egl)
92 eglDebugMessageControlKHR(NULL, NULL);
93 #endif
94
95 pl_gpu_destroy(pl_gl->gpu);
96 gl_release_current(pl_gl);
97 pl_mutex_destroy(&p->lock);
98 pl_free_ptr((void **) ptr);
99 }
100
pl_opengl_create(pl_log log,const struct pl_opengl_params * params)101 pl_opengl pl_opengl_create(pl_log log, const struct pl_opengl_params *params)
102 {
103 params = PL_DEF(params, &pl_opengl_default_params);
104 struct pl_opengl *pl_gl = pl_zalloc_obj(NULL, pl_gl, struct priv);
105 struct priv *p = PL_PRIV(pl_gl);
106 p->params = *params;
107 p->log = log;
108
109 pl_mutex_init_type(&p->lock, PL_MUTEX_RECURSIVE);
110 if (!gl_make_current(pl_gl)) {
111 pl_free(pl_gl);
112 return NULL;
113 }
114
115 int ver = epoxy_gl_version();
116 if (!ver) {
117 PL_FATAL(p, "No OpenGL version detected - make sure an OpenGL context "
118 "is bound to the current thread!");
119 goto error;
120 }
121
122 PL_INFO(p, "Detected OpenGL version strings:");
123 PL_INFO(p, " GL_VERSION: %s", glGetString(GL_VERSION));
124 PL_INFO(p, " GL_VENDOR: %s", glGetString(GL_VENDOR));
125 PL_INFO(p, " GL_RENDERER: %s", glGetString(GL_RENDERER));
126
127 if (pl_msg_test(log, PL_LOG_DEBUG)) {
128 if (ver >= 30) {
129 int num_exts = 0;
130 glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts);
131 PL_DEBUG(p, " GL_EXTENSIONS:");
132 for (int i = 0; i < num_exts; i++) {
133 const char *ext = glGetStringi(GL_EXTENSIONS, i);
134 PL_DEBUG(p, " %s", ext);
135 }
136 } else {
137 PL_DEBUG(p, " GL_EXTENSIONS: %s", glGetString(GL_EXTENSIONS));
138 }
139
140 #ifdef EPOXY_HAS_EGL
141 if (params->egl_display) {
142 PL_DEBUG(p, " EGL_EXTENSIONS: %s",
143 eglQueryString(params->egl_display, EGL_EXTENSIONS));
144 }
145 #endif
146 }
147
148 if (!params->allow_software && gl_is_software()) {
149 PL_FATAL(p, "OpenGL context is suspected to be a software rasterizer, "
150 "but `allow_software` is false.");
151 goto error;
152 }
153
154 if (params->debug) {
155 if (epoxy_has_gl_extension("GL_ARB_debug_output")) {
156 glDebugMessageCallback(debug_cb, log);
157 p->is_debug = true;
158 } else {
159 PL_WARN(p, "OpenGL debugging requested but GL_ARB_debug_output "
160 "unavailable.. ignoring!");
161 }
162
163 #ifdef EPOXY_HAS_EGL
164 if (params->egl_display && epoxy_has_egl_extension(params->egl_display, "EGL_KHR_debug")) {
165 static const EGLAttrib attribs[] = {
166 // Enable everything under the sun, because the `pl_ctx` log
167 // level may change at runtime.
168 EGL_DEBUG_MSG_CRITICAL_KHR, EGL_TRUE,
169 EGL_DEBUG_MSG_ERROR_KHR, EGL_TRUE,
170 EGL_DEBUG_MSG_WARN_KHR, EGL_TRUE,
171 EGL_DEBUG_MSG_INFO_KHR, EGL_TRUE,
172 EGL_NONE,
173 };
174
175 eglDebugMessageControlKHR(debug_cb_egl, attribs);
176 eglLabelObjectKHR(NULL, EGL_OBJECT_THREAD_KHR, NULL, (void *) log);
177 p->is_debug_egl = true;
178 }
179 #endif // EPOXY_HAS_EGL
180 }
181
182 pl_gl->gpu = pl_gpu_create_gl(log, pl_gl, params);
183 if (!pl_gl->gpu)
184 goto error;
185
186 // Restrict version
187 if (params->max_glsl_version) {
188 struct pl_glsl_version *glsl = (struct pl_glsl_version *) &pl_gl->gpu->glsl;
189 glsl->version = PL_MIN(glsl->version, params->max_glsl_version);
190 PL_INFO(p, "Restricting GLSL version to %d... new version is %d",
191 params->max_glsl_version, glsl->version);
192 }
193
194 gl_release_current(pl_gl);
195 return pl_gl;
196
197 error:
198 PL_FATAL(p, "Failed initializing opengl context!");
199 gl_release_current(pl_gl);
200 pl_opengl_destroy((pl_opengl *) &pl_gl);
201 return NULL;
202 }
203
gl_make_current(pl_opengl gl)204 bool gl_make_current(pl_opengl gl)
205 {
206 struct priv *p = PL_PRIV(gl);
207 pl_mutex_lock(&p->lock);
208 if (!p->count && p->params.make_current) {
209 if (!p->params.make_current(p->params.priv)) {
210 PL_ERR(p, "Failed making OpenGL context current on calling thread!");
211 pl_mutex_unlock(&p->lock);
212 return false;
213 }
214 }
215
216 p->count++;
217 return true;
218 }
219
gl_release_current(pl_opengl gl)220 void gl_release_current(pl_opengl gl)
221 {
222 struct priv *p = PL_PRIV(gl);
223 p->count--;
224 if (!p->count && p->params.release_current)
225 p->params.release_current(p->params.priv);
226 pl_mutex_unlock(&p->lock);
227 }
228