fixed the gles1 issue
[sdlhildon] / sdlgles / src / SDL_gles.c
1 /* This file is part of SDL_gles - SDL addon for OpenGL|ES
2  * Copyright (C) 2010 Javier S. Pedro
3  *
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.
8  *
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.
13  *
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/>.
18  */
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <dlfcn.h>
24 #include <assert.h>
25
26 #include <EGL/egl.h>
27
28 #include <SDL.h>
29 #include <SDL_syswm.h>
30
31 #include "SDL_gles.h"
32
33 typedef struct SDL_GLES_ContextPriv
34 {
35         SDL_GLES_Context p;
36
37         EGLConfig egl_config;
38         EGLContext egl_context;
39 } SDL_GLES_ContextPriv;
40
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"
44 };
45
46 static SDL_GLES_Version gl_version = SDL_GLES_VERSION_NONE;
47 static void* gl_handle = NULL;
48 static EGLint egl_major, egl_minor;
49
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[] = {
54         EGL_BUFFER_SIZE,                        0,
55         EGL_RED_SIZE,                           0,
56         EGL_GREEN_SIZE,                         0,
57         EGL_BLUE_SIZE,                          0,
58         EGL_LUMINANCE_SIZE,                     0,
59         EGL_ALPHA_SIZE,                         0,
60         EGL_CONFIG_CAVEAT,                      EGL_DONT_CARE,
61         EGL_CONFIG_ID,                          EGL_DONT_CARE,
62         EGL_DEPTH_SIZE,                         0,
63         EGL_LEVEL,                                      0,
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,
68         EGL_SAMPLES,                            0,
69         EGL_STENCIL_SIZE,                       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,
75         EGL_NONE
76 };
77 static EGLint context_attrib_list[] = {
78         EGL_CONTEXT_CLIENT_VERSION,     1,
79         EGL_NONE
80 };
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;
84
85 static const char * get_error_string(int error) {
86         switch (error) {
87                 case EGL_SUCCESS:
88                         return "EGL_SUCCESS";
89                 case EGL_NOT_INITIALIZED:
90                         return "EGL_NOT_INITIALIZED";
91                 case EGL_BAD_ACCESS:
92                         return "EGL_BAD_ACCESS";
93                 case EGL_BAD_ALLOC:
94                         return "EGL_BAD_ALLOC";
95                 case EGL_BAD_ATTRIBUTE:
96                         return "EGL_BAD_ATTRIBUTE";
97                 case EGL_BAD_CONFIG:
98                         return "EGL_BAD_CONFIG";
99                 case EGL_BAD_CONTEXT:
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";
105                 case EGL_BAD_MATCH:
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";
117                 default:
118                         return "EGL_UNKNOWN_ERROR";
119     }
120 }
121
122 static int set_egl_attrib(EGLenum attrib, EGLint value)
123 {
124         const EGLint a = attrib;
125         int i;
126         for (i = 0; i < attrib_list_size; i++) {
127                 if (attrib_list[i * 2] == a) {
128                         attrib_list[(i*2)+1] = value;
129                         return 0;
130                 }
131         }
132
133         return -1;
134 }
135
136 static EGLint get_egl_attrib(EGLenum attrib)
137 {
138         const EGLint a = attrib;
139         int i;
140         for (i = 0; i < attrib_list_size; i++) {
141                 if (attrib_list[i * 2] == a) {
142                         return attrib_list[(i*2)+1];
143                 }
144         }
145
146         return -1;
147 }
148
149 static int set_egl_context_attrib(EGLenum attrib, EGLint value)
150 {
151         const EGLint a = attrib;
152         int i;
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;
156                         return 0;
157                 }
158         }
159
160         return -1;
161 }
162
163 int SDL_GLES_LoadLibrary(const char *path)
164 {
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. */
167         if (!path) {
168                 path = getenv("SDL_VIDEO_GL_DRIVER");
169                 if (!path) {
170                         switch (gl_version) {
171                                 case SDL_GLES_VERSION_1_1:
172                                 case SDL_GLES_VERSION_2_0:
173                                         path = default_libgl[gl_version];
174                                 break;
175                                 default:
176                                         SDL_SetError("No GL version specific and SDL_VIDEO_GL_DRIVER set");
177                                         return -1;
178                         }
179                 }
180         }
181
182         /* Dynamically load the desired GL library */
183         gl_handle = dlopen(path, RTLD_LAZY|RTLD_GLOBAL);
184         if (!gl_handle) {
185                 SDL_SetError("Failed to open GL library: %s (%s)", path, dlerror());
186                 return -2;
187         }
188
189         return 0;
190 }
191
192 void* SDL_GLES_GetProcAddress(const char *proc)
193 {
194         if (!gl_handle) return NULL;
195         return dlsym(gl_handle, proc);
196 }
197
198 int SDL_GLES_Init(SDL_GLES_Version version)
199 {
200         SDL_SysWMinfo info;
201         EGLBoolean res;
202
203         SDL_VERSION(&info.version);
204         if (SDL_GetWMInfo(&info) != 1) {
205                 SDL_SetError("SDL_gles is incompatible with this SDL version");
206                 return -1;
207         }
208
209         /* We use the SDL GFX display (we're using the GFX window too after all) */
210         display = info.info.x11.gfxdisplay;
211
212         egl_display = eglGetDisplay((EGLNativeDisplayType)display);
213         if (egl_display == EGL_NO_DISPLAY) {
214                 SDL_SetError("EGL found no available displays");
215                 return -2;
216         }
217
218         res = eglInitialize(egl_display, &egl_major, &egl_minor);
219         if (!res) {
220                 SDL_SetError("EGL failed to initialize: %s",
221                         get_error_string(eglGetError()));
222                 return -2;
223         }
224
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:
230                         /* OpenGL|ES 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;
234                         assert(res);
235                         /* default egl_context_client_version is OK */
236                         break;
237                 case SDL_GLES_VERSION_2_0:
238                         /* OpenGL|ES 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;
242                         assert(res);
243                         /* and request GL ES 2.0 contexts */
244                         res = set_egl_context_attrib(EGL_CONTEXT_CLIENT_VERSION, 2) == 0;
245                         assert(res);
246                         break;
247                 default:
248                         SDL_SetError("Unsupported API version");
249                         return -1;
250         }
251
252         res = eglBindAPI(api_to_bind);
253         if (!res) {
254                 SDL_SetError("EGL failed to bind the required API");
255                 return -2;
256         }
257
258         return 0;
259 }
260
261 void SDL_GLES_Quit()
262 {
263         /* Close the loaded GL library (if any) */
264         if (gl_handle) {
265                 dlclose(gl_handle);
266                 gl_handle = NULL;
267         }
268         /* Unallocate most stuff we can unallocate. */
269         if (egl_display != EGL_NO_DISPLAY) {
270                 eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
271                         EGL_NO_CONTEXT);
272
273                 if (cur_context) {
274                         eglDestroyContext(egl_display, cur_context->egl_context);
275                         free(cur_context);
276                         cur_context = 0;
277                 }
278                 if (egl_surface != EGL_NO_SURFACE) {
279                         eglDestroySurface(egl_display, egl_surface);
280                         egl_surface = EGL_NO_SURFACE;
281                 }
282
283                 eglTerminate(egl_display);
284                 egl_display = EGL_NO_DISPLAY;
285         }
286 }
287
288 int SDL_GLES_SetVideoMode()
289 {
290         SDL_SysWMinfo info;
291         EGLBoolean res;
292
293         SDL_VERSION(&info.version);
294         if (SDL_GetWMInfo(&info) != 1) {
295                 SDL_SetError("SDL_gles is incompatible with this SDL version");
296                 return -1;
297         }
298
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,
304                          EGL_NO_CONTEXT);
305                 eglDestroySurface(egl_display, egl_surface);
306                 egl_surface = EGL_NO_SURFACE;
307         }
308
309         /* No current context? Quietly defer surface creation.
310          * Surface will be created on the call to MakeCurrent. */
311         if (!cur_context) {
312                 return 0;
313         }
314
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()));
321                 return -2;
322         }
323
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);
328
329         if (!res) {
330                 SDL_SetError("EGL failed to change current surface: %s",
331                         get_error_string(eglGetError()));
332                 cur_context = NULL;
333                 return -2;
334         }
335
336         return 0;
337 }
338
339 SDL_GLES_Context* SDL_GLES_CreateContext(void)
340 {
341         SDL_GLES_ContextPriv *context = malloc(sizeof(SDL_GLES_ContextPriv));
342         if (!context) {
343                 SDL_Error(SDL_ENOMEM);
344                 return NULL;
345         }
346
347         EGLBoolean res;
348         EGLConfig configs[1];
349         EGLint num_config;
350
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()));
355                 free(context);
356                 return NULL;
357         }
358
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()));
365                 free(context);
366                 return NULL;
367         }
368
369         return (SDL_GLES_Context*) context;
370 }
371
372 void SDL_GLES_DeleteContext(SDL_GLES_Context* c)
373 {
374         SDL_GLES_ContextPriv *context = (SDL_GLES_ContextPriv*)c;
375         if (!context) return;
376
377         if (cur_context == context) {
378                 /* Deleting the active context */
379                 SDL_GLES_MakeCurrent(NULL);
380         }
381
382         eglDestroyContext(egl_display, context->egl_context);
383         free(context);
384 }
385
386 int SDL_GLES_MakeCurrent(SDL_GLES_Context* c)
387 {
388         SDL_GLES_ContextPriv *context = (SDL_GLES_ContextPriv*)c;
389         int res;
390
391         cur_context = context;
392
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. */
398
399         /* TODO Update attrib_list. Make SDL_GLES_GetAttribute work. */
400
401         return 0;
402 }
403
404 void SDL_GLES_SwapBuffers()
405 {
406         eglSwapBuffers(egl_display, egl_surface);
407 }
408