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 static SDL_GLES_Version gl_version = SDL_GLES_VERSION_NONE;
47 static void* gl_handle = NULL;
48 static EGLint egl_major, egl_minor;
50 static Display *display = NULL;
51 static EGLDisplay *egl_display = EGL_NO_DISPLAY;
52 static EGLSurface egl_surface = EGL_NO_SURFACE;
53 static EGLint attrib_list[] = {
58 EGL_LUMINANCE_SIZE, 0,
60 EGL_CONFIG_CAVEAT, EGL_DONT_CARE,
61 EGL_CONFIG_ID, EGL_DONT_CARE,
64 EGL_NATIVE_RENDERABLE, EGL_DONT_CARE,
65 EGL_NATIVE_VISUAL_TYPE, EGL_DONT_CARE,
66 EGL_RENDERABLE_TYPE, 0,
67 EGL_SAMPLE_BUFFERS, 0,
70 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
71 EGL_TRANSPARENT_TYPE, EGL_NONE,
72 EGL_TRANSPARENT_RED_VALUE, EGL_DONT_CARE,
73 EGL_TRANSPARENT_GREEN_VALUE,EGL_DONT_CARE,
74 EGL_TRANSPARENT_BLUE_VALUE, EGL_DONT_CARE,
77 static EGLint context_attrib_list[] = {
78 EGL_CONTEXT_CLIENT_VERSION, 1,
81 static const int attrib_list_size = (sizeof(attrib_list) / sizeof(EGLint)) / 2;
82 static const int context_attrib_list_size = (sizeof(context_attrib_list) / sizeof(EGLint)) / 2;
83 static SDL_GLES_ContextPriv *cur_context = NULL;
85 static const char * get_error_string(int error) {
89 case EGL_NOT_INITIALIZED:
90 return "EGL_NOT_INITIALIZED";
92 return "EGL_BAD_ACCESS";
94 return "EGL_BAD_ALLOC";
95 case EGL_BAD_ATTRIBUTE:
96 return "EGL_BAD_ATTRIBUTE";
98 return "EGL_BAD_CONFIG";
100 return "EGL_BAD_CONTEXT";
101 case EGL_BAD_CURRENT_SURFACE:
102 return "EGL_BAD_CURRENT_SURFACE";
103 case EGL_BAD_DISPLAY:
104 return "EGL_BAD_DISPLAY";
106 return "EGL_BAD_MATCH";
107 case EGL_BAD_NATIVE_PIXMAP:
108 return "EGL_BAD_NATIVE_PIXMAP";
109 case EGL_BAD_NATIVE_WINDOW:
110 return "EGL_BAD_NATIVE_WINDOW";
111 case EGL_BAD_PARAMETER:
112 return "EGL_BAD_PARAMETER";
113 case EGL_BAD_SURFACE:
114 return "EGL_BAD_SURFACE";
115 case EGL_CONTEXT_LOST:
116 return "EGL_CONTEXT_LOST";
118 return "EGL_UNKNOWN_ERROR";
122 static int set_egl_attrib(EGLenum attrib, EGLint value)
124 const EGLint a = attrib;
126 for (i = 0; i < attrib_list_size; i++) {
127 if (attrib_list[i * 2] == a) {
128 attrib_list[(i*2)+1] = value;
136 static EGLint get_egl_attrib(EGLenum attrib)
138 const EGLint a = attrib;
140 for (i = 0; i < attrib_list_size; i++) {
141 if (attrib_list[i * 2] == a) {
142 return attrib_list[(i*2)+1];
149 static int set_egl_context_attrib(EGLenum attrib, EGLint value)
151 const EGLint a = attrib;
153 for (i = 0; i < context_attrib_list_size; i++) {
154 if (context_attrib_list[i * 2] == a) {
155 context_attrib_list[(i*2)+1] = value;
163 int SDL_GLES_LoadLibrary(const char *path)
165 /* If path is NULL, try first to use path from SDL_VIDEO_GL_DRIVER,
166 * otherwise use a sane default depending on selected GLES version. */
168 path = getenv("SDL_VIDEO_GL_DRIVER");
170 switch (gl_version) {
171 case SDL_GLES_VERSION_1_1:
172 case SDL_GLES_VERSION_2_0:
173 path = default_libgl[gl_version];
176 SDL_SetError("No GL version specific and SDL_VIDEO_GL_DRIVER set");
182 /* Dynamically load the desired GL library */
183 gl_handle = dlopen(path, RTLD_LAZY|RTLD_GLOBAL);
185 SDL_SetError("Failed to open GL library: %s (%s)", path, dlerror());
192 void* SDL_GLES_GetProcAddress(const char *proc)
194 if (!gl_handle) return NULL;
195 return dlsym(gl_handle, proc);
198 int SDL_GLES_Init(SDL_GLES_Version version)
203 SDL_VERSION(&info.version);
204 if (SDL_GetWMInfo(&info) != 1) {
205 SDL_SetError("SDL_gles is incompatible with this SDL version");
209 /* We use the SDL GFX display (we're using the GFX window too after all) */
210 display = info.info.x11.gfxdisplay;
212 egl_display = eglGetDisplay((EGLNativeDisplayType)display);
213 if (egl_display == EGL_NO_DISPLAY) {
214 SDL_SetError("EGL found no available displays");
218 res = eglInitialize(egl_display, &egl_major, &egl_minor);
220 SDL_SetError("EGL failed to initialize: %s",
221 get_error_string(eglGetError()));
225 /* Configure some context attributes and bind the required API now. */
226 EGLenum api_to_bind = EGL_OPENGL_ES_API;
227 gl_version = version;
228 switch (gl_version) {
229 case SDL_GLES_VERSION_1_1:
231 api_to_bind = EGL_OPENGL_ES_API;
232 /* filter non ES 1.0 renderable configurations */
233 res = set_egl_attrib(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT) == 0;
235 /* default egl_context_client_version is OK */
237 case SDL_GLES_VERSION_2_0:
239 api_to_bind = EGL_OPENGL_ES_API; /* Note: no EGL_OPENGL_ES2_API */
240 /* filter non ES 2.0 renderable configurations */
241 res = set_egl_attrib(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT) == 0;
243 /* and request GL ES 2.0 contexts */
244 res = set_egl_context_attrib(EGL_CONTEXT_CLIENT_VERSION, 2) == 0;
248 SDL_SetError("Unsupported API version");
252 res = eglBindAPI(api_to_bind);
254 SDL_SetError("EGL failed to bind the required API");
263 /* Close the loaded GL library (if any) */
268 /* Unallocate most stuff we can unallocate. */
269 if (egl_display != EGL_NO_DISPLAY) {
270 eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
274 eglDestroyContext(egl_display, cur_context->egl_context);
278 if (egl_surface != EGL_NO_SURFACE) {
279 eglDestroySurface(egl_display, egl_surface);
280 egl_surface = EGL_NO_SURFACE;
283 eglTerminate(egl_display);
284 egl_display = EGL_NO_DISPLAY;
288 int SDL_GLES_SetVideoMode()
293 SDL_VERSION(&info.version);
294 if (SDL_GetWMInfo(&info) != 1) {
295 SDL_SetError("SDL_gles is incompatible with this SDL version");
299 /* Destroy previous surface, if any. */
300 if (egl_surface != EGL_NO_SURFACE) {
301 /* Ensure the surface is not the current one,
302 * thus freeing memory earlier. */
303 eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
305 eglDestroySurface(egl_display, egl_surface);
306 egl_surface = EGL_NO_SURFACE;
309 /* No current context? Quietly defer surface creation.
310 * Surface will be created on the call to MakeCurrent. */
315 /* Create the new window surface. */
316 egl_surface = eglCreateWindowSurface(egl_display, cur_context->egl_config,
317 (EGLNativeWindowType)info.info.x11.window, NULL);
318 if (egl_surface == EGL_NO_SURFACE) {
319 SDL_SetError("EGL failed to create a window surface: %s",
320 get_error_string(eglGetError()));
324 /* New surface created. Make it active. */
325 assert(cur_context && cur_context->egl_context != EGL_NO_CONTEXT);
326 res = eglMakeCurrent(egl_display, egl_surface, egl_surface,
327 cur_context->egl_context);
330 SDL_SetError("EGL failed to change current surface: %s",
331 get_error_string(eglGetError()));
339 SDL_GLES_Context* SDL_GLES_CreateContext(void)
341 SDL_GLES_ContextPriv *context = malloc(sizeof(SDL_GLES_ContextPriv));
343 SDL_Error(SDL_ENOMEM);
348 EGLConfig configs[1];
351 res = eglChooseConfig(egl_display, attrib_list, configs, 1, &num_config);
352 if (!res || num_config < 1) {
353 SDL_SetError("EGL failed to find any valid config with required attributes: %s",
354 get_error_string(eglGetError()));
359 context->egl_config = configs[0];
360 context->egl_context = eglCreateContext(egl_display, configs[0],
361 EGL_NO_CONTEXT, context_attrib_list);
362 if (context->egl_context == EGL_NO_CONTEXT) {
363 SDL_SetError("EGL failed to create context: %s",
364 get_error_string(eglGetError()));
369 return (SDL_GLES_Context*) context;
372 void SDL_GLES_DeleteContext(SDL_GLES_Context* c)
374 SDL_GLES_ContextPriv *context = (SDL_GLES_ContextPriv*)c;
375 if (!context) return;
377 if (cur_context == context) {
378 /* Deleting the active context */
379 SDL_GLES_MakeCurrent(NULL);
382 eglDestroyContext(egl_display, context->egl_context);
386 int SDL_GLES_MakeCurrent(SDL_GLES_Context* c)
388 SDL_GLES_ContextPriv *context = (SDL_GLES_ContextPriv*)c;
391 cur_context = context;
393 /* SDL_GLES_SetVideoMode() will appropiately clear the current context
394 * (and surface), then create a new surface matching the selected context
395 * config and make both the surface and the context the active ones. */
396 res = SDL_GLES_SetVideoMode();
397 if (res != 0) return res; /* Surface (re-)creation failed. */
399 /* TODO Update attrib_list. Make SDL_GLES_GetAttribute work. */
404 void SDL_GLES_SwapBuffers()
406 eglSwapBuffers(egl_display, egl_surface);