"Initial commit to Gerrit"
[profile/ivi/cogl.git] / cogl / cogl-renderer.c
1 /*
2  * Cogl
3  *
4  * An object oriented GL/GLES Abstraction/Utility Layer
5  *
6  * Copyright (C) 2011 Intel Corporation.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  *
23  * Authors:
24  *   Robert Bragg <robert@linux.intel.com>
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include "cogl-util.h"
35 #include "cogl-internal.h"
36 #include "cogl-private.h"
37 #include "cogl-object.h"
38 #include "cogl-context-private.h"
39
40 #include "cogl-renderer.h"
41 #include "cogl-renderer-private.h"
42 #include "cogl-display-private.h"
43 #include "cogl-winsys-private.h"
44 #include "cogl-winsys-stub-private.h"
45 #include "cogl-config-private.h"
46
47 #ifdef COGL_HAS_EGL_PLATFORM_XLIB_SUPPORT
48 #include "cogl-winsys-egl-x11-private.h"
49 #endif
50 #ifdef COGL_HAS_EGL_PLATFORM_WAYLAND_SUPPORT
51 #include "cogl-winsys-egl-wayland-private.h"
52 #endif
53 #ifdef COGL_HAS_EGL_PLATFORM_KMS_SUPPORT
54 #include "cogl-winsys-egl-kms-private.h"
55 #endif
56 #ifdef COGL_HAS_EGL_PLATFORM_GDL_SUPPORT
57 #include "cogl-winsys-egl-gdl-private.h"
58 #endif
59 #ifdef COGL_HAS_EGL_PLATFORM_ANDROID_SUPPORT
60 #include "cogl-winsys-egl-android-private.h"
61 #endif
62 #ifdef COGL_HAS_EGL_PLATFORM_POWERVR_NULL_SUPPORT
63 #include "cogl-winsys-egl-null-private.h"
64 #endif
65 #ifdef COGL_HAS_GLX_SUPPORT
66 #include "cogl-winsys-glx-private.h"
67 #endif
68 #ifdef COGL_HAS_WGL_SUPPORT
69 #include "cogl-winsys-wgl-private.h"
70 #endif
71 #ifdef COGL_HAS_SDL_SUPPORT
72 #include "cogl-winsys-sdl-private.h"
73 #endif
74
75 #if COGL_HAS_XLIB_SUPPORT
76 #include "cogl-xlib-renderer.h"
77 #endif
78
79 typedef const CoglWinsysVtable *(*CoglWinsysVtableGetter) (void);
80
81 static CoglWinsysVtableGetter _cogl_winsys_vtable_getters[] =
82 {
83 #ifdef COGL_HAS_GLX_SUPPORT
84   _cogl_winsys_glx_get_vtable,
85 #endif
86 #ifdef COGL_HAS_EGL_PLATFORM_XLIB_SUPPORT
87   _cogl_winsys_egl_xlib_get_vtable,
88 #endif
89 #ifdef COGL_HAS_EGL_PLATFORM_WAYLAND_SUPPORT
90   _cogl_winsys_egl_wayland_get_vtable,
91 #endif
92 #ifdef COGL_HAS_EGL_PLATFORM_KMS_SUPPORT
93   _cogl_winsys_egl_kms_get_vtable,
94 #endif
95 #ifdef COGL_HAS_EGL_PLATFORM_GDL_SUPPORT
96   _cogl_winsys_egl_gdl_get_vtable,
97 #endif
98 #ifdef COGL_HAS_EGL_PLATFORM_ANDROID_SUPPORT
99   _cogl_winsys_egl_android_get_vtable,
100 #endif
101 #ifdef COGL_HAS_EGL_PLATFORM_POWERVR_NULL_SUPPORT
102   _cogl_winsys_egl_null_get_vtable,
103 #endif
104 #ifdef COGL_HAS_WGL_SUPPORT
105   _cogl_winsys_wgl_get_vtable,
106 #endif
107 #ifdef COGL_HAS_SDL_SUPPORT
108   _cogl_winsys_sdl_get_vtable,
109 #endif
110   _cogl_winsys_stub_get_vtable,
111 };
112
113 static void _cogl_renderer_free (CoglRenderer *renderer);
114
115 COGL_OBJECT_DEFINE (Renderer, renderer);
116
117 typedef struct _CoglNativeFilterClosure
118 {
119   CoglNativeFilterFunc func;
120   void *data;
121 } CoglNativeFilterClosure;
122
123 GQuark
124 cogl_renderer_error_quark (void)
125 {
126   return g_quark_from_static_string ("cogl-renderer-error-quark");
127 }
128
129 static const CoglWinsysVtable *
130 _cogl_renderer_get_winsys (CoglRenderer *renderer)
131 {
132   return renderer->winsys_vtable;
133 }
134
135 static void
136 native_filter_closure_free (CoglNativeFilterClosure *closure)
137 {
138   g_slice_free (CoglNativeFilterClosure, closure);
139 }
140
141 static void
142 _cogl_renderer_free (CoglRenderer *renderer)
143 {
144   const CoglWinsysVtable *winsys = _cogl_renderer_get_winsys (renderer);
145   winsys->renderer_disconnect (renderer);
146
147 #ifndef HAVE_DIRECTLY_LINKED_GL_LIBRARY
148   if (renderer->libgl_module)
149     g_module_close (renderer->libgl_module);
150 #endif
151
152   g_slist_foreach (renderer->event_filters,
153                    (GFunc) native_filter_closure_free,
154                    NULL);
155   g_slist_free (renderer->event_filters);
156
157   g_free (renderer);
158 }
159
160 CoglRenderer *
161 cogl_renderer_new (void)
162 {
163   CoglRenderer *renderer = g_new0 (CoglRenderer, 1);
164
165   _cogl_init ();
166
167   renderer->connected = FALSE;
168   renderer->event_filters = NULL;
169
170 #ifdef COGL_HAS_XLIB_SUPPORT
171   renderer->xlib_enable_event_retrieval = TRUE;
172 #endif
173
174   return _cogl_renderer_object_new (renderer);
175 }
176
177 #if COGL_HAS_XLIB_SUPPORT
178 void
179 cogl_xlib_renderer_set_foreign_display (CoglRenderer *renderer,
180                                         Display *xdisplay)
181 {
182   _COGL_RETURN_IF_FAIL (cogl_is_renderer (renderer));
183
184   /* NB: Renderers are considered immutable once connected */
185   _COGL_RETURN_IF_FAIL (!renderer->connected);
186
187   renderer->foreign_xdpy = xdisplay;
188
189   /* If the application is using a foreign display then we can assume
190      it will also do its own event retrieval */
191   cogl_xlib_renderer_set_event_retrieval_enabled (renderer, FALSE);
192 }
193
194 Display *
195 cogl_xlib_renderer_get_foreign_display (CoglRenderer *renderer)
196 {
197   _COGL_RETURN_VAL_IF_FAIL (cogl_is_renderer (renderer), NULL);
198
199   return renderer->foreign_xdpy;
200 }
201
202 void
203 cogl_xlib_renderer_set_event_retrieval_enabled (CoglRenderer *renderer,
204                                                 gboolean enable)
205 {
206   _COGL_RETURN_IF_FAIL (cogl_is_renderer (renderer));
207   /* NB: Renderers are considered immutable once connected */
208   _COGL_RETURN_IF_FAIL (!renderer->connected);
209
210   renderer->xlib_enable_event_retrieval = enable;
211 }
212 #endif /* COGL_HAS_XLIB_SUPPORT */
213
214 gboolean
215 cogl_renderer_check_onscreen_template (CoglRenderer *renderer,
216                                        CoglOnscreenTemplate *onscreen_template,
217                                        GError **error)
218 {
219   CoglDisplay *display;
220
221   if (!cogl_renderer_connect (renderer, error))
222     return FALSE;
223
224   display = cogl_display_new (renderer, onscreen_template);
225   if (!cogl_display_setup (display, error))
226     {
227       cogl_object_unref (display);
228       return FALSE;
229     }
230
231   cogl_object_unref (display);
232
233   return TRUE;
234 }
235
236 static gboolean
237 _cogl_renderer_choose_driver (CoglRenderer *renderer,
238                               GError **error)
239 {
240   const char *driver_name = g_getenv ("COGL_DRIVER");
241   const char *libgl_name;
242
243   if (!driver_name)
244     driver_name = _cogl_config_driver;
245
246 #ifdef HAVE_COGL_GL
247   if (renderer->driver_override == COGL_DRIVER_GL ||
248       (renderer->driver_override == COGL_DRIVER_ANY &&
249        (driver_name == NULL || !g_ascii_strcasecmp (driver_name, "gl"))))
250     {
251       renderer->driver = COGL_DRIVER_GL;
252       libgl_name = COGL_GL_LIBNAME;
253       goto found;
254     }
255 #endif
256
257 #ifdef HAVE_COGL_GLES2
258   if (renderer->driver_override == COGL_DRIVER_GLES2 ||
259       (renderer->driver_override == COGL_DRIVER_ANY &&
260        (driver_name == NULL || !g_ascii_strcasecmp (driver_name, "gles2"))))
261     {
262       renderer->driver = COGL_DRIVER_GLES2;
263       libgl_name = COGL_GLES2_LIBNAME;
264       goto found;
265     }
266 #endif
267
268 #ifdef HAVE_COGL_GLES
269   if (renderer->driver_override == COGL_DRIVER_GLES1 ||
270       (renderer->driver_override == COGL_DRIVER_ANY &&
271        (driver_name == NULL || !g_ascii_strcasecmp (driver_name, "gles1"))))
272     {
273       renderer->driver = COGL_DRIVER_GLES1;
274       libgl_name = COGL_GLES1_LIBNAME;
275       goto found;
276     }
277 #endif
278
279   g_set_error (error,
280                COGL_DRIVER_ERROR,
281                COGL_DRIVER_ERROR_NO_SUITABLE_DRIVER_FOUND,
282                "No suitable driver found");
283   return FALSE;
284
285  found:
286
287 #ifndef HAVE_DIRECTLY_LINKED_GL_LIBRARY
288
289   renderer->libgl_module = g_module_open (libgl_name,
290                                           G_MODULE_BIND_LAZY);
291
292   if (renderer->libgl_module == NULL)
293     {
294       g_set_error (error, COGL_DRIVER_ERROR,
295                    COGL_DRIVER_ERROR_FAILED_TO_LOAD_LIBRARY,
296                    "Failed to dynamically open the GL library \"%s\"",
297                    libgl_name);
298       return FALSE;
299     }
300
301 #endif /* HAVE_DIRECTLY_LINKED_GL_LIBRARY */
302
303   return TRUE;
304 }
305
306 /* Final connection API */
307
308 gboolean
309 cogl_renderer_connect (CoglRenderer *renderer, GError **error)
310 {
311   int i;
312   GString *error_message;
313
314   if (renderer->connected)
315     return TRUE;
316
317   /* The driver needs to be chosen before connecting the renderer
318      because eglInitialize requires the library containing the GL API
319      to be loaded before its called */
320   if (!_cogl_renderer_choose_driver (renderer, error))
321     return FALSE;
322
323   error_message = g_string_new ("");
324   for (i = 0; i < G_N_ELEMENTS (_cogl_winsys_vtable_getters); i++)
325     {
326       const CoglWinsysVtable *winsys = _cogl_winsys_vtable_getters[i]();
327       GError *tmp_error = NULL;
328       GList *l;
329       gboolean constraints_failed = FALSE;
330
331       if (renderer->winsys_id_override != COGL_WINSYS_ID_ANY)
332         {
333           if (renderer->winsys_id_override != winsys->id)
334             continue;
335         }
336       else
337         {
338           char *user_choice = getenv ("COGL_RENDERER");
339           if (!user_choice)
340             user_choice = _cogl_config_renderer;
341           if (user_choice &&
342               g_ascii_strcasecmp (winsys->name, user_choice) != 0)
343             continue;
344         }
345
346       for (l = renderer->constraints; l; l = l->next)
347         {
348           CoglRendererConstraint constraint = GPOINTER_TO_UINT (l->data);
349           if (!(winsys->constraints & constraint))
350             {
351               constraints_failed = TRUE;
352               break;
353             }
354         }
355       if (constraints_failed)
356         continue;
357
358       /* At least temporarily we will associate this winsys with
359        * the renderer in-case ->renderer_connect calls API that
360        * wants to query the current winsys... */
361       renderer->winsys_vtable = winsys;
362
363       if (!winsys->renderer_connect (renderer, &tmp_error))
364         {
365           g_string_append_c (error_message, '\n');
366           g_string_append (error_message, tmp_error->message);
367           g_error_free (tmp_error);
368         }
369       else
370         {
371           renderer->connected = TRUE;
372           g_string_free (error_message, TRUE);
373           return TRUE;
374         }
375     }
376
377   if (!renderer->connected)
378     {
379       renderer->winsys_vtable = NULL;
380       g_set_error (error, COGL_WINSYS_ERROR,
381                    COGL_WINSYS_ERROR_INIT,
382                    "Failed to connected to any renderer: %s",
383                    error_message->str);
384       g_string_free (error_message, TRUE);
385       return FALSE;
386     }
387
388   return TRUE;
389 }
390
391 CoglFilterReturn
392 _cogl_renderer_handle_native_event (CoglRenderer *renderer,
393                                     void *event)
394 {
395   GSList *l, *next;
396
397   /* Pass the event on to all of the registered filters in turn */
398   for (l = renderer->event_filters; l; l = next)
399     {
400       CoglNativeFilterClosure *closure = l->data;
401
402       /* The next pointer is taken now so that we can handle the
403          closure being removed during emission */
404       next = l->next;
405
406       if (closure->func (event, closure->data) == COGL_FILTER_REMOVE)
407         return COGL_FILTER_REMOVE;
408     }
409
410   /* If the backend for the renderer also wants to see the events, it
411      should just register its own filter */
412
413   return COGL_FILTER_CONTINUE;
414 }
415
416 void
417 _cogl_renderer_add_native_filter (CoglRenderer *renderer,
418                                   CoglNativeFilterFunc func,
419                                   void *data)
420 {
421   CoglNativeFilterClosure *closure;
422
423   closure = g_slice_new (CoglNativeFilterClosure);
424   closure->func = func;
425   closure->data = data;
426
427   renderer->event_filters = g_slist_prepend (renderer->event_filters, closure);
428 }
429
430 void
431 _cogl_renderer_remove_native_filter (CoglRenderer *renderer,
432                                      CoglNativeFilterFunc func,
433                                      void *data)
434 {
435   GSList *l, *prev = NULL;
436
437   for (l = renderer->event_filters; l; prev = l, l = l->next)
438     {
439       CoglNativeFilterClosure *closure = l->data;
440
441       if (closure->func == func && closure->data == data)
442         {
443           native_filter_closure_free (closure);
444           if (prev)
445             prev->next = g_slist_delete_link (prev->next, l);
446           else
447             renderer->event_filters =
448               g_slist_delete_link (renderer->event_filters, l);
449           break;
450         }
451     }
452 }
453
454 void
455 cogl_renderer_set_winsys_id (CoglRenderer *renderer,
456                              CoglWinsysID winsys_id)
457 {
458   _COGL_RETURN_IF_FAIL (!renderer->connected);
459
460   renderer->winsys_id_override = winsys_id;
461 }
462
463 CoglWinsysID
464 cogl_renderer_get_winsys_id (CoglRenderer *renderer)
465 {
466   _COGL_RETURN_VAL_IF_FAIL (renderer->connected, 0);
467
468   return renderer->winsys_vtable->id;
469 }
470
471 void *
472 _cogl_renderer_get_proc_address (CoglRenderer *renderer,
473                                  const char *name)
474 {
475   const CoglWinsysVtable *winsys = _cogl_renderer_get_winsys (renderer);
476
477   return winsys->renderer_get_proc_address (renderer, name);
478 }
479
480 int
481 cogl_renderer_get_n_fragment_texture_units (CoglRenderer *renderer)
482 {
483   int n = 0;
484
485   _COGL_GET_CONTEXT (ctx, 0);
486
487 #if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES2)
488   if (ctx->driver == COGL_DRIVER_GL || ctx->driver == COGL_DRIVER_GLES2)
489     GE (ctx, glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, &n));
490 #endif
491
492   return n;
493 }
494
495 void
496 cogl_renderer_add_constraint (CoglRenderer *renderer,
497                               CoglRendererConstraint constraint)
498 {
499   g_return_if_fail (!renderer->connected);
500   renderer->constraints = g_list_prepend (renderer->constraints,
501                                           GUINT_TO_POINTER (constraint));
502 }
503
504 void
505 cogl_renderer_remove_constraint (CoglRenderer *renderer,
506                                  CoglRendererConstraint constraint)
507 {
508   g_return_if_fail (!renderer->connected);
509   renderer->constraints = g_list_remove (renderer->constraints,
510                                          GUINT_TO_POINTER (constraint));
511 }
512
513 void
514 cogl_renderer_set_driver (CoglRenderer *renderer,
515                           CoglDriver driver)
516 {
517   _COGL_RETURN_IF_FAIL (!renderer->connected);
518   renderer->driver_override = driver;
519 }
520
521 CoglDriver
522 cogl_renderer_get_driver (CoglRenderer *renderer)
523 {
524   _COGL_RETURN_VAL_IF_FAIL (renderer->connected, 0);
525
526   return renderer->driver;
527 }