tizen 2.3.1 release
[framework/graphics/cairo.git] / src / cairo-egl-context.c
1 /* cairo - a vector graphics library with display and print output
2  *
3  * Copyright © 2009 Eric Anholt
4  * Copyright © 2009 Chris Wilson
5  * Copyright © 2005 Red Hat, Inc
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it either under the terms of the GNU Lesser General Public
9  * License version 2.1 as published by the Free Software Foundation
10  * (the "LGPL") or, at your option, under the terms of the Mozilla
11  * Public License Version 1.1 (the "MPL"). If you do not alter this
12  * notice, a recipient may use your version of this file under either
13  * the MPL or the LGPL.
14  *
15  * You should have received a copy of the LGPL along with this library
16  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
18  * You should have received a copy of the MPL along with this library
19  * in the file COPYING-MPL-1.1
20  *
21  * The contents of this file are subject to the Mozilla Public License
22  * Version 1.1 (the "License"); you may not use this file except in
23  * compliance with the License. You may obtain a copy of the License at
24  * http://www.mozilla.org/MPL/
25  *
26  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
27  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
28  * the specific language governing rights and limitations.
29  *
30  * The Original Code is the cairo graphics library.
31  *
32  * The Initial Developer of the Original Code is Red Hat, Inc.
33  *
34  * Contributor(s):
35  *      Carl Worth <cworth@cworth.org>
36  *      Chris Wilson <chris@chris-wilson.co.uk>
37  */
38
39 #include "cairoint.h"
40
41 #include "cairo-gl-private.h"
42
43 #include "cairo-error-private.h"
44
45 #if CAIRO_HAS_EVASGL_SURFACE && CAIRO_HAS_GLESV2_SURFACE
46 extern void glActiveTexture (GLenum texture);
47 extern void glBindTexture (GLenum target, GLuint texture);
48 extern void glBlendFunc (GLenum sfactor, GLenum dfactor);
49 extern void glBlendFuncSeparate (GLenum srcRGB, GLenum dstRGB,
50                                  GLenum srcAlpha, GLenum dstAlpha);
51 extern void glClear (GLbitfield mask);
52 extern void glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
53 extern void glClearStencil (GLint s);
54 extern void glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
55 extern void glDeleteTextures (GLsizei n, const GLuint *textures);
56 extern void glDepthMask (GLboolean flag);
57 extern void glDisable (GLenum cap);
58 extern void glDrawArrays (GLenum mode, GLint first, GLsizei count);
59 extern void glDrawElements (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
60 extern void glEnable (GLenum cap);
61 extern void glFlush (void);
62 extern void glGenTextures (GLsizei n, GLuint *textures);
63 extern void glGetBooleanv (GLenum pname, GLboolean *data);
64 extern GLenum glGetError (void);
65 extern void glGetFloatv (GLenum pname, GLfloat *data);
66 extern void glGetIntegerv (GLenum pname, GLint *data);
67 extern const unsigned char* glGetString (GLenum pname);
68 extern void glPixelStorei (GLenum pname, GLint param);
69 extern void glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height,
70                           GLenum format, GLenum type, GLvoid *data);
71 extern void glScissor (GLint x, GLint y, GLsizei width, GLsizei height);
72 extern void glStencilFunc (GLenum func, GLint ref, GLuint mask);
73 extern void glStencilMask (GLuint mask);
74 extern void glStencilOp (GLenum sfail, GLenum dpfail, GLenum dppass);
75 extern void glTexSubImage2D (GLenum target, GLint level,
76                              GLint xoffset, GLint yoffset,
77                              GLsizei width, GLsizei height,
78                              GLenum format, GLenum type, const GLvoid *data);
79 extern void glTexImage2D (GLenum target, GLint level, GLenum internalformat,
80                           GLsizei width, GLsizei height,
81                           GLint border, GLenum format,
82                           GLenum type, const GLvoid *data);
83 extern void glTexParameteri (GLenum target, GLenum pname, GLint param);
84 extern void glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
85 #endif
86
87 typedef struct _cairo_egl_context {
88     cairo_gl_context_t base;
89
90     EGLDisplay display;
91     EGLContext context;
92
93     EGLSurface dummy_surface;
94     EGLSurface current_surface;
95
96     EGLDisplay previous_display;
97     EGLContext previous_context;
98     EGLSurface previous_surface;
99 } cairo_egl_context_t;
100
101 typedef struct _cairo_egl_surface {
102     cairo_gl_surface_t base;
103
104     EGLSurface egl;
105 } cairo_egl_surface_t;
106
107
108 static cairo_bool_t
109 _context_acquisition_changed_egl_state (cairo_egl_context_t *ctx,
110                                         EGLSurface current_surface)
111 {
112     return ctx->previous_context != ctx->context ||
113            ctx->previous_surface != current_surface ||
114            ctx->previous_display != ctx->display;
115 }
116
117 static EGLSurface
118 _egl_get_current_surface (cairo_egl_context_t *ctx)
119 {
120     if (ctx->base.current_target == NULL ||
121         _cairo_gl_surface_is_texture (ctx->base.current_target)) {
122         return  ctx->dummy_surface;
123     }
124
125     return ((cairo_egl_surface_t *) ctx->base.current_target)->egl;
126 }
127
128 static void
129 _egl_query_current_state (cairo_egl_context_t *ctx)
130 {
131     ctx->previous_surface = eglGetCurrentSurface (EGL_DRAW);
132     ctx->previous_context = eglGetCurrentContext ();
133     ctx->previous_display = eglGetCurrentDisplay ();
134
135     /* If any of the values were none, assume they are all none. Not all
136        drivers seem well behaved when it comes to using these values across
137        multiple threads. */
138     if (ctx->previous_surface == EGL_NO_SURFACE ||
139         ctx->previous_context == EGL_NO_CONTEXT || ctx->previous_display == EGL_NO_DISPLAY) {
140         ctx->previous_surface = EGL_NO_SURFACE;
141         ctx->previous_context = EGL_NO_CONTEXT;
142         ctx->previous_display = EGL_NO_DISPLAY;
143     }
144 }
145
146 static void
147 _egl_acquire (void *abstract_ctx)
148 {
149     cairo_egl_context_t *ctx = abstract_ctx;
150     EGLSurface current_surface = _egl_get_current_surface (ctx);
151
152     _egl_query_current_state (ctx);
153     if (!_context_acquisition_changed_egl_state (ctx, current_surface))
154         return;
155
156     _cairo_gl_context_reset (&ctx->base);
157     eglMakeCurrent (ctx->display,
158                     current_surface, current_surface, ctx->context);
159
160     ctx->current_surface = current_surface;
161 }
162
163 static void
164 _egl_release (void *abstract_ctx)
165 {
166     cairo_egl_context_t *ctx = abstract_ctx;
167     if (!ctx->base.thread_aware ||
168         !_context_acquisition_changed_egl_state (ctx,
169                                                  _egl_get_current_surface (ctx))) {
170         return;
171     }
172
173     eglMakeCurrent (ctx->display,
174                     EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
175     ctx->current_surface = EGL_NO_SURFACE;
176 }
177
178 static void
179 _egl_make_current (void *abstract_ctx,
180                    cairo_gl_surface_t *abstract_surface)
181 {
182     cairo_egl_context_t *ctx = abstract_ctx;
183     cairo_egl_surface_t *surface = (cairo_egl_surface_t *) abstract_surface;
184
185     if (surface->egl != ctx->current_surface) {
186         eglMakeCurrent(ctx->display, surface->egl, surface->egl, ctx->context);
187         ctx->current_surface = surface->egl;
188     }
189 }
190
191 static void
192 _egl_swap_buffers (void *abstract_ctx,
193                    cairo_gl_surface_t *abstract_surface)
194 {
195     cairo_egl_context_t *ctx = abstract_ctx;
196     cairo_egl_surface_t *surface = (cairo_egl_surface_t *) abstract_surface;
197
198     eglSwapBuffers (ctx->display, surface->egl);
199 }
200
201 static void
202 _egl_destroy (void *abstract_ctx)
203 {
204     cairo_egl_context_t *ctx = abstract_ctx;
205
206     eglMakeCurrent (ctx->display,
207                     EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
208     if (ctx->dummy_surface != EGL_NO_SURFACE)
209         eglDestroySurface (ctx->display, ctx->dummy_surface);
210 }
211
212 static cairo_bool_t
213 _egl_make_current_surfaceless(cairo_egl_context_t *ctx)
214 {
215     const char *extensions;
216
217     extensions = eglQueryString(ctx->display, EGL_EXTENSIONS);
218     if (extensions == NULL)
219         return FALSE;
220
221     if (strstr(extensions, "EGL_KHR_surfaceless_context") == NULL &&
222         strstr(extensions, "EGL_KHR_surfaceless_opengl") == NULL)
223         return FALSE;
224
225     if (!eglMakeCurrent(ctx->display,
226                         EGL_NO_SURFACE, EGL_NO_SURFACE, ctx->context))
227         return FALSE;
228
229     return TRUE;
230 }
231
232 static cairo_gl_generic_func_t
233 _cairo_egl_get_proc_address (void *data, const char *name)
234 {
235     int i;
236     struct {
237         cairo_gl_generic_func_t func;
238         const char *name;
239     } func_map[] = {
240     { (cairo_gl_generic_func_t)glActiveTexture, "glActiveTexture"       },
241     { (cairo_gl_generic_func_t)glBindTexture,   "glBindTexture"         },
242     { (cairo_gl_generic_func_t)glBlendFunc,     "glBlendFunc"           },
243     { (cairo_gl_generic_func_t)glBlendFuncSeparate,"glBlendFuncSeparate"},
244     { (cairo_gl_generic_func_t)glClear,         "glClear"               },
245     { (cairo_gl_generic_func_t)glClearColor,    "glClearColor"          },
246     { (cairo_gl_generic_func_t)glClearStencil,  "glClearStencil"        },
247     { (cairo_gl_generic_func_t)glColorMask,     "glColorMask"           },
248     { (cairo_gl_generic_func_t)glDeleteTextures,"glDeleteTextures"      },
249     { (cairo_gl_generic_func_t)glDepthMask,     "glDepthMask"           },
250     { (cairo_gl_generic_func_t)glDisable,       "glDisable"             },
251     { (cairo_gl_generic_func_t)glDrawArrays,    "glDrawArrays"          },
252     { (cairo_gl_generic_func_t)glDrawElements,  "glDrawElements"        },
253     { (cairo_gl_generic_func_t)glEnable,        "glEnable"              },
254     { (cairo_gl_generic_func_t)glGenTextures,   "glGenTextures"         },
255     { (cairo_gl_generic_func_t)glGetBooleanv,   "glGetBooleanv"         },
256     { (cairo_gl_generic_func_t)glGetError,      "glGetError"            },
257     { (cairo_gl_generic_func_t)glGetFloatv,     "glGetFloatv"           },
258     { (cairo_gl_generic_func_t)glGetIntegerv,   "glGetIntegerv"         },
259     { (cairo_gl_generic_func_t)glGetString,     "glGetString"           },
260     { (cairo_gl_generic_func_t)glFlush,         "glFlush"               },
261     { (cairo_gl_generic_func_t)glPixelStorei,   "glPixelStorei"         },
262     { (cairo_gl_generic_func_t)glReadPixels,    "glReadPixels"          },
263     { (cairo_gl_generic_func_t)glScissor,       "glScissor"             },
264     { (cairo_gl_generic_func_t)glStencilFunc,   "glStencilFunc"         },
265     { (cairo_gl_generic_func_t)glStencilMask,   "glStencilMask"         },
266     { (cairo_gl_generic_func_t)glStencilOp,     "glStencilOp"           },
267     { (cairo_gl_generic_func_t)glTexImage2D,    "glTexImage2D"          },
268     { (cairo_gl_generic_func_t)glTexSubImage2D, "glTexSubImage2D"       },
269     { (cairo_gl_generic_func_t)glTexParameteri, "glTexParameteri"       },
270     { (cairo_gl_generic_func_t)glViewport,      "glViewport"            },
271     { NULL,                                      NULL                   }
272     };
273
274     for (i = 0; func_map[i].name; i++) {
275         if (! strncmp (func_map[i].name, name, strlen(name)))
276             return func_map[i].func;
277     }
278
279     return eglGetProcAddress (name);
280 }
281
282 cairo_device_t *
283 cairo_egl_device_create (EGLDisplay dpy, EGLContext egl)
284 {
285     cairo_egl_context_t *ctx;
286     cairo_status_t status;
287     int attribs[] = {
288         EGL_WIDTH, 1,
289         EGL_HEIGHT, 1,
290         EGL_NONE,
291     };
292     EGLConfig config;
293     EGLint numConfigs;
294
295     ctx = calloc (1, sizeof (cairo_egl_context_t));
296     if (unlikely (ctx == NULL))
297         return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
298
299     ctx->display = dpy;
300     ctx->context = egl;
301
302     ctx->base.acquire = _egl_acquire;
303     ctx->base.release = _egl_release;
304     ctx->base.make_current = _egl_make_current;
305     ctx->base.swap_buffers = _egl_swap_buffers;
306     ctx->base.destroy = _egl_destroy;
307
308     /* We are about the change the current state of EGL, so we should
309      * query the pre-existing surface now instead of later. */
310     _egl_query_current_state (ctx);
311
312     if (!_egl_make_current_surfaceless (ctx)) {
313         /* Fall back to dummy surface, meh. */
314         EGLint config_attribs[] = {
315             EGL_CONFIG_ID, 0,
316             EGL_NONE
317         };
318
319         /*
320          * In order to be able to make an egl context current when using a
321          * pbuffer surface, that surface must have been created with a config
322          * that is compatible with the context config. For Mesa, this means
323          * that the configs must be the same.
324          */
325         eglQueryContext (dpy, egl, EGL_CONFIG_ID, &config_attribs[1]);
326         eglChooseConfig (dpy, config_attribs, &config, 1, &numConfigs);
327
328         ctx->dummy_surface = eglCreatePbufferSurface (dpy, config, attribs);
329         if (ctx->dummy_surface == NULL) {
330             free (ctx);
331             return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
332         }
333
334         if (!eglMakeCurrent (dpy, ctx->dummy_surface, ctx->dummy_surface, egl)) {
335             free (ctx);
336             return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
337         }
338     }
339
340     status = _cairo_gl_dispatch_init (&ctx->base.dispatch,
341                                       _cairo_egl_get_proc_address, NULL);
342     if (unlikely (status)) {
343         free (ctx);
344         return _cairo_gl_context_create_in_error (status);
345     }
346
347     status = _cairo_gl_context_init (&ctx->base);
348     if (unlikely (status)) {
349         if (ctx->dummy_surface != EGL_NO_SURFACE)
350             eglDestroySurface (dpy, ctx->dummy_surface);
351         free (ctx);
352         return _cairo_gl_context_create_in_error (status);
353     }
354
355     eglMakeCurrent (dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
356
357     ctx->current_surface = EGL_NO_SURFACE;
358
359     return &ctx->base.base;
360 }
361
362 cairo_surface_t *
363 cairo_gl_surface_create_for_egl (cairo_device_t *device,
364                                  EGLSurface      egl,
365                                  int             width,
366                                  int             height)
367 {
368     cairo_egl_surface_t *surface;
369
370     if (unlikely (device->status))
371         return _cairo_surface_create_in_error (device->status);
372
373     if (device->backend->type != CAIRO_DEVICE_TYPE_GL)
374         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
375
376     if (width <= 0 || height <= 0)
377         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
378
379     surface = calloc (1, sizeof (cairo_egl_surface_t));
380     if (unlikely (surface == NULL))
381         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
382
383     _cairo_gl_surface_init (device, &surface->base,
384                             CAIRO_CONTENT_COLOR_ALPHA, width, height);
385     surface->egl = egl;
386
387     return &surface->base.base;
388 }
389
390 static cairo_bool_t is_egl_device (cairo_device_t *device)
391 {
392     return (device->backend != NULL &&
393             device->backend->type == CAIRO_DEVICE_TYPE_GL);
394 }
395
396 static cairo_egl_context_t *to_egl_context (cairo_device_t *device)
397 {
398     return (cairo_egl_context_t *) device;
399 }
400
401 EGLDisplay
402 cairo_egl_device_get_display (cairo_device_t *device)
403 {
404     if (! is_egl_device (device)) {
405         _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
406         return EGL_NO_DISPLAY;
407     }
408
409     return to_egl_context (device)->display;
410 }
411
412 cairo_public EGLContext
413 cairo_egl_device_get_context (cairo_device_t *device)
414 {
415     if (! is_egl_device (device)) {
416         _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
417         return EGL_NO_CONTEXT;
418     }
419
420     return to_egl_context (device)->context;
421 }