1 /* This file is part of SDL_gles - SDL addon for OpenGL|ES
2 * Copyright (C) 2010 Javier S. Pedro
4 * This library 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 3 of the License, or (at your option) any later version.
9 * This library 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 GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA or see <http://www.gnu.org/licenses/>.
29 #include <SDL_syswm.h>
33 typedef struct SDL_GLES_ContextPriv
38 EGLContext egl_context;
39 } SDL_GLES_ContextPriv;
41 static const char * default_libgl[] = {
42 [SDL_GLES_VERSION_1_1] = "/usr/lib/libGLES_CM.so",
43 [SDL_GLES_VERSION_2_0] = "/usr/lib/libGLESv2.so"
46 /** SDL GFX display */
47 static Display *display = NULL;
48 /** EGLDisplay for the above X11 display */
49 static EGLDisplay *egl_display = EGL_NO_DISPLAY;
50 /** The current surface. Recreated by SDL_GLES_SetVideoMode(). */
51 static EGLSurface egl_surface = EGL_NO_SURFACE;
52 /** A pointer to the current active context. */
53 static SDL_GLES_ContextPriv *cur_context = NULL;
55 /** The desired GLES version, as selected by the SDL_GLES_Init() call. */
56 static SDL_GLES_Version gl_version = SDL_GLES_VERSION_NONE;
57 /** A handle to the dynamically loaded GL library. */
58 static void* gl_handle = NULL;
60 static EGLint egl_major, egl_minor;
62 /** Your average countof() macro. */
63 #define countof(a) (sizeof(a)/sizeof(a[0]))
65 /** List of EGLConfig attributes we care about;
66 * Used for filtering; modified by SDL_GLES_Get/SetAttribute(). */
67 static EGLint attrib_list[] = {
68 #define A(number, attrib, default_value) \
69 attrib, default_value,
70 #include "attribs.inc"
74 /** A enum which maps A_EGL_* attrib constants to attrib_list positions. */
76 #define A(number, attrib, default_value) \
77 A_ ## attrib = (number * 2),
78 #include "attribs.inc"
81 static EGLint context_attrib_list[] = {
82 EGL_CONTEXT_CLIENT_VERSION, 1,
86 static const char * get_error_string(int error) {
90 case EGL_NOT_INITIALIZED:
91 return "EGL_NOT_INITIALIZED";
93 return "EGL_BAD_ACCESS";
95 return "EGL_BAD_ALLOC";
96 case EGL_BAD_ATTRIBUTE:
97 return "EGL_BAD_ATTRIBUTE";
99 return "EGL_BAD_CONFIG";
100 case EGL_BAD_CONTEXT:
101 return "EGL_BAD_CONTEXT";
102 case EGL_BAD_CURRENT_SURFACE:
103 return "EGL_BAD_CURRENT_SURFACE";
104 case EGL_BAD_DISPLAY:
105 return "EGL_BAD_DISPLAY";
107 return "EGL_BAD_MATCH";
108 case EGL_BAD_NATIVE_PIXMAP:
109 return "EGL_BAD_NATIVE_PIXMAP";
110 case EGL_BAD_NATIVE_WINDOW:
111 return "EGL_BAD_NATIVE_WINDOW";
112 case EGL_BAD_PARAMETER:
113 return "EGL_BAD_PARAMETER";
114 case EGL_BAD_SURFACE:
115 return "EGL_BAD_SURFACE";
116 case EGL_CONTEXT_LOST:
117 return "EGL_CONTEXT_LOST";
119 return "EGL_UNKNOWN_ERROR";
123 static inline void set_egl_attrib(attrib_enum attrib, EGLint value)
125 const unsigned int i = (unsigned int)attrib + 1;
126 assert(i < countof(attrib_list));
127 attrib_list[i] = value;
130 static inline EGLint get_egl_attrib(attrib_enum attrib)
132 const unsigned int i = (unsigned int)attrib + 1;
133 assert(i < countof(attrib_list));
134 return attrib_list[i];
137 static inline void set_egl_context_attrib(EGLenum attrib, EGLint value)
139 /* Only one attribute supported here. */
140 assert(attrib == EGL_CONTEXT_CLIENT_VERSION);
141 context_attrib_list[1] = value;
144 int SDL_GLES_LoadLibrary(const char *path)
146 /* If path is NULL, try first to use path from SDL_VIDEO_GL_DRIVER,
147 * otherwise use a sane default depending on selected GLES version. */
149 path = getenv("SDL_VIDEO_GL_DRIVER");
151 switch (gl_version) {
152 case SDL_GLES_VERSION_1_1:
153 case SDL_GLES_VERSION_2_0:
154 path = default_libgl[gl_version];
157 SDL_SetError("No GL version specific and SDL_VIDEO_GL_DRIVER set");
163 /* Dynamically load the desired GL library */
164 gl_handle = dlopen(path, RTLD_LAZY|RTLD_GLOBAL);
166 SDL_SetError("Failed to open GL library: %s (%s)", path, dlerror());
173 void* SDL_GLES_GetProcAddress(const char *proc)
175 if (!gl_handle) return NULL;
176 return dlsym(gl_handle, proc);
179 int SDL_GLES_Init(SDL_GLES_Version version)
184 SDL_VERSION(&info.version);
185 if (SDL_GetWMInfo(&info) != 1) {
186 SDL_SetError("SDL_gles is incompatible with this SDL version");
190 /* We use the SDL GFX display (we're using the GFX window too after all) */
191 display = info.info.x11.gfxdisplay;
193 egl_display = eglGetDisplay((EGLNativeDisplayType)display);
194 if (egl_display == EGL_NO_DISPLAY) {
195 SDL_SetError("EGL found no available displays");
199 res = eglInitialize(egl_display, &egl_major, &egl_minor);
201 SDL_SetError("EGL failed to initialize: %s",
202 get_error_string(eglGetError()));
206 /* Configure some context attributes and bind the required API now. */
207 EGLenum api_to_bind = EGL_OPENGL_ES_API;
208 gl_version = version;
209 switch (gl_version) {
210 case SDL_GLES_VERSION_1_1:
212 api_to_bind = EGL_OPENGL_ES_API;
213 /* filter non ES 1.0 renderable configurations */
214 set_egl_attrib(A_EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT);
215 /* default egl_context_client_version is OK */
217 case SDL_GLES_VERSION_2_0:
219 api_to_bind = EGL_OPENGL_ES_API; /* Note: no EGL_OPENGL_ES2_API */
220 /* filter non ES 2.0 renderable configurations */
221 set_egl_attrib(A_EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT);
222 /* and request GL ES 2.0 contexts */
223 set_egl_context_attrib(EGL_CONTEXT_CLIENT_VERSION, 2);
226 SDL_SetError("Unsupported API version");
230 res = eglBindAPI(api_to_bind);
232 SDL_SetError("EGL failed to bind the required API");
241 /* Close the loaded GL library (if any) */
246 /* Unallocate most stuff we can unallocate. */
247 if (egl_display != EGL_NO_DISPLAY) {
248 eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
252 eglDestroyContext(egl_display, cur_context->egl_context);
256 if (egl_surface != EGL_NO_SURFACE) {
257 eglDestroySurface(egl_display, egl_surface);
258 egl_surface = EGL_NO_SURFACE;
261 eglTerminate(egl_display);
262 egl_display = EGL_NO_DISPLAY;
266 int SDL_GLES_SetVideoMode()
271 SDL_VERSION(&info.version);
272 if (SDL_GetWMInfo(&info) != 1) {
273 SDL_SetError("SDL_gles is incompatible with this SDL version");
277 /* Destroy previous surface, if any. */
278 if (egl_surface != EGL_NO_SURFACE) {
279 /* Ensure the surface is not the current one,
280 * thus freeing memory earlier. */
281 eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
283 eglDestroySurface(egl_display, egl_surface);
284 egl_surface = EGL_NO_SURFACE;
287 /* No current context? Quietly defer surface creation.
288 * Surface will be created on the call to MakeCurrent. */
293 /* Create the new window surface. */
294 egl_surface = eglCreateWindowSurface(egl_display, cur_context->egl_config,
295 (EGLNativeWindowType)info.info.x11.window, NULL);
296 if (egl_surface == EGL_NO_SURFACE) {
297 SDL_SetError("EGL failed to create a window surface: %s",
298 get_error_string(eglGetError()));
302 /* New surface created. Make it active. */
303 assert(cur_context && cur_context->egl_context != EGL_NO_CONTEXT);
304 res = eglMakeCurrent(egl_display, egl_surface, egl_surface,
305 cur_context->egl_context);
308 SDL_SetError("EGL failed to change current surface: %s",
309 get_error_string(eglGetError()));
317 SDL_GLES_Context* SDL_GLES_CreateContext(void)
319 SDL_GLES_ContextPriv *context = malloc(sizeof(SDL_GLES_ContextPriv));
321 SDL_Error(SDL_ENOMEM);
326 EGLConfig configs[1];
329 res = eglChooseConfig(egl_display, attrib_list, configs, 1, &num_config);
330 if (!res || num_config < 1) {
331 SDL_SetError("EGL failed to find any valid config with required attributes: %s",
332 get_error_string(eglGetError()));
337 context->egl_config = configs[0];
338 context->egl_context = eglCreateContext(egl_display, configs[0],
339 EGL_NO_CONTEXT, context_attrib_list);
340 if (context->egl_context == EGL_NO_CONTEXT) {
341 SDL_SetError("EGL failed to create context: %s",
342 get_error_string(eglGetError()));
347 return (SDL_GLES_Context*) context;
350 void SDL_GLES_DeleteContext(SDL_GLES_Context* c)
352 SDL_GLES_ContextPriv *context = (SDL_GLES_ContextPriv*)c;
353 if (!context) return;
355 if (cur_context == context) {
356 /* Deleting the active context */
357 SDL_GLES_MakeCurrent(NULL);
360 eglDestroyContext(egl_display, context->egl_context);
364 int SDL_GLES_MakeCurrent(SDL_GLES_Context* c)
366 SDL_GLES_ContextPriv *context = (SDL_GLES_ContextPriv*)c;
369 cur_context = context;
371 /* SDL_GLES_SetVideoMode() will appropiately clear the current context
372 * (and surface), then create a new surface matching the selected context
373 * config and make both the surface and the context the active ones. */
374 res = SDL_GLES_SetVideoMode();
375 if (res != 0) return res; /* Surface (re-)creation failed. */
380 void SDL_GLES_SwapBuffers()
382 eglSwapBuffers(egl_display, egl_surface);
385 /** A simple map between SDL_GLES_* attributes and EGL ones.
386 * More abstraction layers is always good.
388 static const attrib_enum attrib_map[] = {
389 [SDL_GLES_BUFFER_SIZE] = A_EGL_BUFFER_SIZE,
390 [SDL_GLES_RED_SIZE] = A_EGL_RED_SIZE,
391 [SDL_GLES_GREEN_SIZE] = A_EGL_GREEN_SIZE,
392 [SDL_GLES_BLUE_SIZE] = A_EGL_BLUE_SIZE,
393 [SDL_GLES_ALPHA_SIZE] = A_EGL_ALPHA_SIZE,
394 [SDL_GLES_LUMINANCE_SIZE] = A_EGL_LUMINANCE_SIZE,
395 [SDL_GLES_DEPTH_SIZE] = A_EGL_DEPTH_SIZE,
396 [SDL_GLES_STENCIL_SIZE] = A_EGL_STENCIL_SIZE,
399 int SDL_GLES_SetAttribute(SDL_GLES_Attr attr, int value)
401 if (attr >= countof(attrib_map)) return -1;
402 attrib_enum list_attr = attrib_map[attr];
403 set_egl_attrib(list_attr, value);
407 int SDL_GLES_GetAttribute(SDL_GLES_Attr attr, int *value)
409 if (attr >= countof(attrib_map)) return -1;
410 attrib_enum list_attr = attrib_map[attr];
412 EGLenum egl_attr = attrib_list[list_attr];
413 EGLint egl_value = 0;
414 EGLBoolean res = eglGetConfigAttrib(egl_display,
415 cur_context->egl_config, egl_attr, &egl_value);
420 printf("Failed: %s\n", get_error_string(eglGetError()));
424 *value = get_egl_attrib(list_attr);