summaryrefslogtreecommitdiff
path: root/src/cairo-gl-device.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-gl-device.c')
-rwxr-xr-xsrc/cairo-gl-device.c1092
1 files changed, 1092 insertions, 0 deletions
diff --git a/src/cairo-gl-device.c b/src/cairo-gl-device.c
new file mode 100755
index 000000000..a22ac63bb
--- /dev/null
+++ b/src/cairo-gl-device.c
@@ -0,0 +1,1092 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ * Copyright © 2010 Linaro Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Benjamin Otte <otte@gnome.org>
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Eric Anholt <eric@anholt.net>
+ * Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-gl-private.h"
+#include "cairo-rtree-private.h"
+
+#if CAIRO_HAS_EVASGL_SURFACE
+#include "cairo-evas-gl.h"
+#endif
+
+#define MAX_MSAA_SAMPLES 4
+
+cairo_int_status_t
+_cairo_gl_image_cache_init (cairo_gl_context_t *ctx, int width, int height,
+ cairo_gl_image_cache_t **image_cache)
+{
+ cairo_surface_t *cache_surface = _cairo_gl_surface_create_scratch (ctx,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ width, height);
+ if (unlikely (cache_surface->status)) {
+ cairo_surface_destroy (cache_surface);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ _cairo_surface_release_device_reference (cache_surface);
+ *image_cache = _cairo_malloc (sizeof (cairo_gl_image_cache_t));
+ if (*image_cache == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ (*image_cache)->surface = (cairo_gl_surface_t *)cache_surface;
+ (*image_cache)->surface->supports_msaa = FALSE;
+
+ _cairo_rtree_init (&((*image_cache)->rtree), width, height,
+ IMAGE_CACHE_MIN_SIZE,
+ sizeof (cairo_gl_image_t),
+ _cairo_gl_image_node_destroy);
+
+ (*image_cache)->copy_success = TRUE;
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+void
+_cairo_gl_image_cache_fini (cairo_gl_context_t *ctx)
+{
+ if (ctx->image_cache) {
+ _cairo_rtree_fini (&ctx->image_cache->rtree);
+ cairo_surface_destroy (&ctx->image_cache->surface->base);
+ }
+ free (ctx->image_cache);
+}
+
+static void
+_gl_lock (void *device)
+{
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *) device;
+
+ ctx->acquire (ctx);
+}
+
+static void
+_gl_unlock (void *device)
+{
+ cairo_gl_context_t *ctx = (cairo_gl_context_t *) device;
+
+ ctx->release (ctx);
+}
+
+static cairo_status_t
+_gl_flush (void *device)
+{
+ cairo_gl_context_t *ctx;
+ cairo_status_t status;
+
+ status = _cairo_gl_context_acquire (device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ _cairo_gl_composite_flush (ctx);
+
+ _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE);
+ _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK);
+
+ if (ctx->clip_region) {
+ cairo_region_destroy (ctx->clip_region);
+ ctx->clip_region = NULL;
+ }
+
+ ctx->current_target = NULL;
+ ctx->current_operator = -1;
+ ctx->vertex_size = 0;
+ ctx->pre_shader = NULL;
+ _cairo_gl_set_shader (ctx, NULL);
+
+ ctx->dispatch.BindBuffer (GL_ARRAY_BUFFER, 0);
+
+ _cairo_gl_context_reset (ctx);
+
+ _disable_scissor_buffer (ctx);
+
+ if (ctx->states_cache.blend_enabled == TRUE ) {
+ ctx->dispatch.Disable (GL_BLEND);
+ ctx->states_cache.blend_enabled = FALSE;
+ }
+
+ return _cairo_gl_context_release (ctx, status);
+}
+
+static void
+_gl_finish (void *device)
+{
+ cairo_gl_context_t *ctx = device;
+ int n;
+
+ _gl_lock (device);
+
+ _cairo_cache_fini (&ctx->gradients);
+
+ _cairo_gl_context_fini_shaders (ctx);
+
+ for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
+ _cairo_gl_glyph_cache_fini (ctx, &ctx->glyph_cache[n]);
+
+
+ _cairo_gl_image_cache_fini (ctx);
+
+ _gl_unlock (device);
+}
+
+static void
+_gl_destroy (void *device)
+{
+ int n;
+
+ cairo_gl_context_t *ctx = device;
+
+ ctx->acquire (ctx);
+
+ if(ctx->glyph_mask) {
+ cairo_surface_destroy (&ctx->glyph_mask->base);
+ ctx->glyph_mask = NULL;
+ }
+
+ for (n = 0; n < 2; n++) {
+ if (ctx->source_scratch_surfaces[n])
+ cairo_surface_destroy (&ctx->source_scratch_surfaces[n]->base);
+ if (ctx->mask_scratch_surfaces[n])
+ cairo_surface_destroy (&ctx->mask_scratch_surfaces[n]->base);
+ if (ctx->shadow_scratch_surfaces[n])
+ cairo_surface_destroy (&ctx->shadow_scratch_surfaces[n]->base);
+ }
+ if (ctx->shadow_scratch_surfaces[2])
+ cairo_surface_destroy (&ctx->shadow_scratch_surfaces[2]->base);
+
+ for (n = 0; n < 4; n++) {
+ if (ctx->shadow_masks[n])
+ cairo_surface_destroy (&ctx->shadow_masks[n]->base);
+ }
+
+ while (! cairo_list_is_empty (&ctx->fonts)) {
+ cairo_gl_font_t *font;
+
+ font = cairo_list_first_entry (&ctx->fonts,
+ cairo_gl_font_t,
+ link);
+
+ cairo_list_del (&font->base.link);
+ cairo_list_del (&font->link);
+ free (font);
+ }
+
+ _cairo_array_fini (&ctx->tristrip_indices);
+
+ cairo_region_destroy (ctx->clip_region);
+
+ free (ctx->vb);
+
+ ctx->destroy (ctx);
+
+ free (ctx);
+}
+
+static const cairo_device_backend_t _cairo_gl_device_backend = {
+ CAIRO_DEVICE_TYPE_GL,
+
+ _gl_lock,
+ _gl_unlock,
+
+ _gl_flush, /* flush */
+ _gl_finish,
+ _gl_destroy,
+};
+
+static cairo_bool_t
+_cairo_gl_msaa_compositor_enabled (void)
+{
+ const char *env = getenv ("CAIRO_GL_COMPOSITOR");
+ return env && strcmp(env, "msaa") == 0;
+}
+
+static cairo_bool_t
+test_can_read_bgra (cairo_gl_context_t *ctx, cairo_gl_flavor_t gl_flavor)
+{
+ /* Desktop GL always supports BGRA formats. */
+ if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ return TRUE;
+
+ assert (gl_flavor == CAIRO_GL_FLAVOR_ES2 ||
+ gl_flavor == CAIRO_GL_FLAVOR_ES3);
+
+ /* For OpenGL ES we have to look for the specific extension and BGRA only
+ * matches cairo's integer packed bytes on little-endian machines. */
+ if (!_cairo_is_little_endian())
+ return FALSE;
+ return _cairo_gl_has_extension (&ctx->dispatch, "EXT_read_format_bgra");
+}
+
+cairo_status_t
+_cairo_gl_context_init (cairo_gl_context_t *ctx)
+{
+ cairo_status_t status;
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ int gl_version = _cairo_gl_get_version (dispatch);
+ cairo_gl_flavor_t gl_flavor = _cairo_gl_get_flavor (dispatch);
+ int n;
+
+ cairo_bool_t is_desktop = gl_flavor == CAIRO_GL_FLAVOR_DESKTOP;
+ cairo_bool_t is_gles = (gl_flavor == CAIRO_GL_FLAVOR_ES2 ||
+ gl_flavor == CAIRO_GL_FLAVOR_ES3);
+
+ _cairo_device_init (&ctx->base, &_cairo_gl_device_backend);
+
+ /* XXX The choice of compositor should be made automatically at runtime.
+ * However, it is useful to force one particular compositor whilst
+ * testing.
+ */
+ if (_cairo_gl_msaa_compositor_enabled ())
+ ctx->compositor = _cairo_gl_msaa_compositor_get ();
+ else
+ ctx->compositor = _cairo_gl_span_compositor_get ();
+
+
+ ctx->thread_aware = TRUE;
+ ctx->has_angle_multisampling = FALSE;
+
+ memset (ctx->glyph_cache, 0, sizeof (ctx->glyph_cache));
+ cairo_list_init (&ctx->fonts);
+
+ /* Support only GL version >= 1.3 */
+ if (gl_version < CAIRO_GL_VERSION_ENCODE (1, 3))
+ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+ /* Check for required extensions */
+ if (is_desktop) {
+ if (_cairo_gl_has_extension (&ctx->dispatch, "GL_ARB_texture_non_power_of_two")) {
+ ctx->tex_target = GL_TEXTURE_2D;
+ ctx->has_npot_repeat = TRUE;
+ } else if (_cairo_gl_has_extension (&ctx->dispatch, "GL_ARB_texture_rectangle")) {
+ ctx->tex_target = GL_TEXTURE_RECTANGLE;
+ ctx->has_npot_repeat = FALSE;
+ } else
+ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+ } else {
+ ctx->tex_target = GL_TEXTURE_2D;
+ if (_cairo_gl_has_extension (&ctx->dispatch, "GL_OES_texture_npot") ||
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_IMG_texture_npot"))
+ ctx->has_npot_repeat = TRUE;
+ else
+ ctx->has_npot_repeat = FALSE;
+ }
+
+ if (is_desktop && gl_version < CAIRO_GL_VERSION_ENCODE (2, 1) &&
+ ! _cairo_gl_has_extension (&ctx->dispatch, "GL_ARB_pixel_buffer_object"))
+ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+ if (is_gles && ! _cairo_gl_has_extension (&ctx->dispatch, "GL_EXT_texture_format_BGRA8888"))
+ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+
+ ctx->has_map_buffer =
+ is_desktop || (is_gles && _cairo_gl_has_extension (&ctx->dispatch, "GL_OES_mapbuffer"));
+
+ ctx->can_read_bgra = test_can_read_bgra (ctx, gl_flavor);
+
+ ctx->has_mesa_pack_invert =
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_MESA_pack_invert");
+
+ ctx->has_packed_depth_stencil =
+ (is_desktop && _cairo_gl_has_extension (&ctx->dispatch, "GL_EXT_packed_depth_stencil")) ||
+ (is_gles && _cairo_gl_has_extension (&ctx->dispatch, "GL_OES_packed_depth_stencil"));
+
+ ctx->num_samples = 1;
+ ctx->msaa_type = CAIRO_GL_NONE_MULTISAMPLE_TO_TEXTURE;
+
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (is_desktop && ctx->has_packed_depth_stencil &&
+ (gl_version >= CAIRO_GL_VERSION_ENCODE (3, 0) ||
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_ARB_framebuffer_object") ||
+ (_cairo_gl_has_extension (&ctx->dispatch, "GL_EXT_framebuffer_blit") &&
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_EXT_framebuffer_multisample")))) {
+ ctx->dispatch.GetIntegerv(GL_MAX_SAMPLES_EXT, &ctx->num_samples);
+ }
+#endif
+
+#if (CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE) && GL_MAX_SAMPLES_EXT
+ if (is_gles && ctx->has_packed_depth_stencil &&
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_EXT_multisampled_render_to_texture")) {
+ ctx->dispatch.GetIntegerv(GL_MAX_SAMPLES_EXT, &ctx->num_samples);
+ ctx->msaa_type = CAIRO_GL_EXT_MULTISAMPLE_TO_TEXTURE;
+ }
+#endif
+
+#if (CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE) && GL_MAX_SAMPLES_IMG
+ if (ctx->msaa_type == CAIRO_GL_NONE_MULTISAMPLE_TO_TEXTURE &&
+ is_gles &&
+ ctx->has_packed_depth_stencil &&
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_IMG_multisampled_render_to_texture")) {
+ ctx->dispatch.GetIntegerv(GL_MAX_SAMPLES_IMG, &ctx->num_samples);
+ ctx->msaa_type = CAIRO_GL_IMG_MULTISAMPLE_TO_TEXTURE;
+ }
+#endif
+
+#if (CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE) && GL_MAX_SAMPLES_ANGLE
+ if (ctx->msaa_type == CAIRO_GL_NONE_MULTISAMPLE_TO_TEXTURE &&
+ is_gles &&
+ ctx->has_packed_depth_stencil &&
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_ANGLE_framebuffer_blit") &&
+ _cairo_gl_has_extension (&ctx->dispatch, "GL_ANGLE_framebuffer_multisample")) {
+ ctx->dispatch.GetIntegerv(GL_MAX_SAMPLES_ANGLE, &ctx->num_samples);
+ ctx->has_angle_multisampling = TRUE;
+ }
+#endif
+
+#if CAIRO_HAS_GLESV3_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->msaa_type == CAIRO_GL_NONE_MULTISAMPLE_TO_TEXTURE &&
+ is_gles && ctx->has_packed_depth_stencil) {
+ ctx->dispatch.GetIntegerv(GL_MAX_SAMPLES, &ctx->num_samples);
+ /* this is work around for evasgl. At this moment, if
+ we still get samples == 1, it means gles2 does not have any
+ support for extensions we have supported
+ */
+ if (gl_flavor == CAIRO_GL_FLAVOR_ES2)
+ ctx->num_samples = 1;
+ }
+#endif
+
+ /* we always use renderbuffer for rendering in glesv3 */
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 ||
+ (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ ctx->has_angle_multisampling))
+ ctx->supports_msaa = TRUE;
+ else
+ ctx->supports_msaa = ctx->num_samples > 1;
+ if (ctx->num_samples > MAX_MSAA_SAMPLES)
+ ctx->num_samples = MAX_MSAA_SAMPLES;
+
+
+ ctx->current_operator = -1;
+ ctx->gl_flavor = gl_flavor;
+
+ status = _cairo_gl_context_init_shaders (ctx);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_cache_init (&ctx->gradients,
+ _cairo_gl_gradient_equal,
+ NULL,
+ (cairo_destroy_func_t) _cairo_gl_gradient_destroy,
+ CAIRO_GL_GRADIENT_CACHE_SIZE);
+ if (unlikely (status))
+ return status;
+
+ ctx->vb = malloc (CAIRO_GL_VBO_SIZE);
+ if (unlikely (ctx->vb == NULL)) {
+ _cairo_cache_fini (&ctx->gradients);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ ctx->primitive_type = CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES;
+ _cairo_array_init (&ctx->tristrip_indices, sizeof (unsigned short));
+
+ /* PBO for any sort of texture upload */
+ dispatch->GenBuffers (1, &ctx->texture_load_pbo);
+
+ ctx->max_framebuffer_size = 0;
+ ctx->dispatch.GetIntegerv (GL_MAX_RENDERBUFFER_SIZE, &ctx->max_framebuffer_size);
+ ctx->max_texture_size = 0;
+ ctx->dispatch.GetIntegerv (GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size);
+ ctx->max_textures = 0;
+ ctx->dispatch.GetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, &ctx->max_textures);
+
+ for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
+ _cairo_gl_glyph_cache_init (&ctx->glyph_cache[n]);
+
+ ctx->image_cache = NULL;
+
+ for (n = 0; n < 2; n++) {
+ ctx->source_scratch_surfaces[n] = NULL;
+ ctx->mask_scratch_surfaces[n] = NULL;
+ ctx->shadow_scratch_surfaces[n] = NULL;
+ }
+
+ for (n = 0; n < 4; n++)
+ ctx->shadow_masks[n] = NULL;
+
+ ctx->source_scratch_in_use = FALSE;
+
+ _cairo_gl_context_reset (ctx);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_gl_context_activate (cairo_gl_context_t *ctx,
+ cairo_gl_tex_t tex_unit)
+{
+ if (ctx->max_textures <= (GLint) tex_unit) {
+ if (tex_unit < 2) {
+ _cairo_gl_composite_flush (ctx);
+ _cairo_gl_context_destroy_operand (ctx, ctx->max_textures - 1);
+ }
+ if (ctx->states_cache.active_texture != ctx->max_textures - 1) {
+ ctx->dispatch.ActiveTexture (ctx->max_textures - 1);
+ ctx->states_cache.active_texture = ctx->max_textures - 1;
+ }
+ } else {
+ if (ctx->states_cache.active_texture != GL_TEXTURE0 + tex_unit) {
+ ctx->dispatch.ActiveTexture (GL_TEXTURE0 + tex_unit);
+ ctx->states_cache.active_texture = GL_TEXTURE0 + tex_unit;
+ }
+ }
+}
+
+static GLenum
+_get_depth_stencil_format (cairo_gl_context_t *ctx)
+{
+ /* This is necessary to properly handle the situation where both
+ OpenGL and OpenGLES are active and returning a sane default. */
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ return GL_DEPTH_STENCIL;
+#endif
+
+#if CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ return GL_DEPTH24_STENCIL8_OES;
+#endif
+
+#if CAIRO_HAS_GL_SURFACE
+ return GL_DEPTH_STENCIL;
+#elif CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ return GL_DEPTH24_STENCIL8_OES;
+#elif CAIRO_HAS_GLESV3_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ return GL_DEPTH24_STENCIL8;
+#endif
+}
+
+static void
+_cairo_gl_clear_framebuffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ return;
+
+ if (_cairo_gl_surface_is_scratch (ctx, surface)) {
+ _disable_scissor_buffer (ctx);
+ _disable_stencil_buffer (ctx);
+ ctx->dispatch.Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ }
+}
+
+#if CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+static void
+_cairo_gl_ensure_msaa_gles_framebuffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ if (ctx->has_angle_multisampling)
+ return;
+
+ if (surface->msaa_active)
+ return;
+
+ ctx->dispatch.FramebufferTexture2DMultisample(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ ctx->tex_target,
+ surface->tex,
+ 0,
+ ctx->num_samples);
+
+ /* From now on MSAA will always be active on this surface. */
+ surface->msaa_active = TRUE;
+}
+#endif
+
+void
+_cairo_gl_ensure_framebuffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ GLenum status;
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+
+ if (likely (surface->fb))
+ return;
+
+ /* Create a framebuffer object wrapping the texture so that we can render
+ * to it.
+ */
+ dispatch->GenFramebuffers (1, &surface->fb);
+ dispatch->BindFramebuffer (GL_FRAMEBUFFER, surface->fb);
+
+ /* Unlike for desktop GL we only maintain one multisampling framebuffer
+ for OpenGLES since the EXT_multisampled_render_to_texture extension
+ does not require an explicit multisample resolution. */
+#if CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (surface->supports_msaa && _cairo_gl_msaa_compositor_enabled () &&
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ ! ctx->has_angle_multisampling) {
+ _cairo_gl_ensure_msaa_gles_framebuffer (ctx, surface);
+ } else
+#endif
+ dispatch->FramebufferTexture2D (GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ ctx->tex_target,
+ surface->tex,
+ 0);
+
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP &&
+ ctx->dispatch.DrawBuffer &&
+ ctx->dispatch.ReadBuffer) {
+ ctx->dispatch.DrawBuffer (GL_COLOR_ATTACHMENT0);
+ ctx->dispatch.ReadBuffer (GL_COLOR_ATTACHMENT0);
+ }
+#endif
+
+ status = dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ const char *str;
+ switch (status) {
+ //case GL_FRAMEBUFFER_UNDEFINED: str= "undefined"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: str= "incomplete attachment"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: str= "incomplete/missing attachment"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: str= "incomplete draw buffer"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: str= "incomplete read buffer"; break;
+ case GL_FRAMEBUFFER_UNSUPPORTED: str= "unsupported"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: str= "incomplete multiple"; break;
+ default: str = "unknown error"; break;
+ }
+
+ fprintf (stderr,
+ "destination is framebuffer incomplete: %s [%#x]\n",
+ str, status);
+ }
+}
+
+static void
+_cairo_gl_ensure_multisampling (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ GLenum rgba;
+
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ ! ctx->has_angle_multisampling)
+ return;
+
+ assert (surface->supports_msaa);
+
+ if (surface->msaa_fb)
+ return;
+
+ /* We maintain a separate framebuffer for multisampling operations.
+ This allows us to do a fast paint to the non-multisampling framebuffer
+ when mulitsampling is disabled. */
+ ctx->dispatch.GenFramebuffers (1, &surface->msaa_fb);
+ ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->msaa_fb);
+ ctx->dispatch.GenRenderbuffers (1, &surface->msaa_rb);
+ ctx->dispatch.BindRenderbuffer (GL_RENDERBUFFER, surface->msaa_rb);
+
+ /* FIXME: For now we assume that textures passed from the outside have GL_RGBA
+ format, but eventually we need to expose a way for the API consumer to pass
+ this information. */
+#if CAIRO_HAS_GLESV3_SURFACE || CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+#if CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_GLESV3_SURFACE
+ rgba = GL_RGBA8;
+#else
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ rgba = GL_RGBA;
+ else
+ rgba = GL_RGBA8;
+#endif
+#else
+ rgba = GL_RGBA;
+#endif
+ ctx->dispatch.RenderbufferStorageMultisample (GL_RENDERBUFFER,
+ ctx->num_samples,
+ rgba,
+ surface->width,
+ surface->height);
+
+ ctx->dispatch.FramebufferRenderbuffer (GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER,
+ surface->msaa_rb);
+
+ if (ctx->dispatch.CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_rb);
+ surface->msaa_rb = 0;
+ ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_fb);
+ surface->msaa_fb = 0;
+ return;
+ }
+
+ /* Cairo surfaces start out initialized to transparent (black) */
+ _disable_scissor_buffer (ctx);
+ ctx->dispatch.ClearColor (0, 0, 0, 0);
+ // reset cached clear colors
+ memset (&ctx->states_cache.clear_red, 0, sizeof (GLclampf) * 4);
+ ctx->dispatch.Clear (GL_COLOR_BUFFER_BIT);
+}
+
+static cairo_bool_t
+_cairo_gl_ensure_msaa_depth_stencil_buffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+ if (surface->msaa_depth_stencil)
+ return TRUE;
+
+ _cairo_gl_ensure_multisampling (ctx, surface);
+
+ dispatch->GenRenderbuffers (1, &surface->msaa_depth_stencil);
+ dispatch->BindRenderbuffer (GL_RENDERBUFFER,
+ surface->msaa_depth_stencil);
+
+ dispatch->RenderbufferStorageMultisample (GL_RENDERBUFFER,
+ ctx->num_samples,
+ _get_depth_stencil_format (ctx),
+ surface->width,
+ surface->height);
+
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV3_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP ||
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) {
+ dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER,
+ GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER,
+ surface->msaa_depth_stencil);
+ }
+#endif
+
+#if CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) {
+ dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER,
+ GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER,
+ surface->msaa_depth_stencil);
+ dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER,
+ GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER,
+ surface->msaa_depth_stencil);
+ }
+#endif
+
+ if (dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ dispatch->DeleteRenderbuffers (1, &surface->msaa_depth_stencil);
+ surface->msaa_depth_stencil = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static cairo_bool_t
+_cairo_gl_ensure_depth_stencil_buffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
+
+ if (surface->depth_stencil)
+ return TRUE;
+
+ _cairo_gl_ensure_framebuffer (ctx, surface);
+
+ dispatch->GenRenderbuffers (1, &surface->depth_stencil);
+ dispatch->BindRenderbuffer (GL_RENDERBUFFER, surface->depth_stencil);
+ dispatch->RenderbufferStorage (GL_RENDERBUFFER,
+ _get_depth_stencil_format (ctx),
+ surface->width, surface->height);
+
+ dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, surface->depth_stencil);
+ dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER, surface->depth_stencil);
+ if (dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ dispatch->DeleteRenderbuffers (1, &surface->depth_stencil);
+ surface->depth_stencil = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+cairo_bool_t
+_cairo_gl_ensure_stencil (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ if (! _cairo_gl_surface_is_texture (surface))
+ return TRUE; /* best guess for now, will check later */
+ if (! ctx->has_packed_depth_stencil)
+ return FALSE;
+
+ if (surface->msaa_active)
+ return _cairo_gl_ensure_msaa_depth_stencil_buffer (ctx, surface);
+ else
+ return _cairo_gl_ensure_depth_stencil_buffer (ctx, surface);
+}
+
+/*
+ * Stores a parallel projection transformation in matrix 'm',
+ * using column-major order.
+ *
+ * This is equivalent to:
+ *
+ * glLoadIdentity()
+ * gluOrtho2D()
+ *
+ * The calculation for the ortho tranformation was taken from the
+ * mesa source code.
+ */
+static void
+_gl_identity_ortho (GLfloat *m,
+ GLfloat left, GLfloat right,
+ GLfloat bottom, GLfloat top)
+{
+#define M(row,col) m[col*4+row]
+ M(0,0) = 2.f / (right - left);
+ M(0,1) = 0.f;
+ M(0,2) = 0.f;
+ M(0,3) = -(right + left) / (right - left);
+
+ M(1,0) = 0.f;
+ M(1,1) = 2.f / (top - bottom);
+ M(1,2) = 0.f;
+ M(1,3) = -(top + bottom) / (top - bottom);
+
+ M(2,0) = 0.f;
+ M(2,1) = 0.f;
+ M(2,2) = -1.f;
+ M(2,3) = 0.f;
+
+ M(3,0) = 0.f;
+ M(3,1) = 0.f;
+ M(3,2) = 0.f;
+ M(3,3) = 1.f;
+#undef M
+}
+
+static void
+bind_multisample_framebuffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ cairo_bool_t stencil_test_enabled, scissor_test_enabled;
+ cairo_bool_t has_stencil_cache;
+ GLbitfield mask;
+
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) {
+ stencil_test_enabled = ctx->states_cache.stencil_test_enabled;
+ scissor_test_enabled = ctx->states_cache.scissor_test_enabled;
+
+ has_stencil_cache = surface->clip_on_stencil_buffer ? TRUE : FALSE;
+ mask = GL_COLOR_BUFFER_BIT;
+ }
+#endif
+ assert (surface->supports_msaa);
+
+ _cairo_gl_ensure_framebuffer (ctx, surface);
+ _cairo_gl_ensure_multisampling (ctx, surface);
+
+ if (surface->msaa_active) {
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ ctx->dispatch.Enable (GL_MULTISAMPLE);
+
+#endif
+ ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->msaa_fb);
+ return;
+ }
+
+ _cairo_gl_composite_flush (ctx);
+
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ /* we must disable scissor and stencil test */
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) {
+ _disable_stencil_buffer (ctx);
+ _disable_scissor_buffer (ctx);
+
+ ctx->dispatch.Enable (GL_MULTISAMPLE);
+
+ if (has_stencil_cache)
+ mask |= GL_STENCIL_BUFFER_BIT;
+
+ /* The last time we drew to the surface, we were not using multisampling,
+ so we need to blit from the non-multisampling framebuffer into the
+ multisampling framebuffer. */
+ ctx->dispatch.BindFramebuffer (GL_DRAW_FRAMEBUFFER, surface->msaa_fb);
+ ctx->dispatch.BindFramebuffer (GL_READ_FRAMEBUFFER, surface->fb);
+ ctx->dispatch.BlitFramebuffer (0, 0, surface->width, surface->height,
+ 0, 0, surface->width, surface->height,
+ mask, GL_NEAREST);
+ surface->content_synced = TRUE;
+ }
+#endif
+ ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->msaa_fb);
+
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) {
+ /* re-enable stencil and scissor test */
+ if (scissor_test_enabled)
+ _enable_scissor_buffer (ctx);
+ if (stencil_test_enabled)
+ _enable_stencil_buffer (ctx);
+ }
+#endif
+}
+
+static void
+bind_singlesample_framebuffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface)
+{
+ cairo_bool_t has_stencil_cache = surface->clip_on_stencil_buffer ? TRUE : FALSE;
+ cairo_bool_t stencil_test_enabled = ctx->states_cache.stencil_test_enabled;
+ cairo_bool_t scissor_test_enabled = ctx->states_cache.scissor_test_enabled;
+ GLbitfield mask = GL_COLOR_BUFFER_BIT;
+
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ ! ctx->has_angle_multisampling)
+ return;
+
+ _cairo_gl_ensure_framebuffer (ctx, surface);
+
+ if (! surface->msaa_active) {
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_FLAVOR
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ ctx->dispatch.Disable (GL_MULTISAMPLE);
+#endif
+ ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb);
+ return;
+ }
+
+ _cairo_gl_composite_flush (ctx);
+
+ /* we must disable scissor and stencil test */
+ _disable_stencil_buffer (ctx);
+ _disable_scissor_buffer (ctx);
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_FLAVOR
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
+ ctx->dispatch.Disable (GL_MULTISAMPLE);
+#endif
+
+ if (has_stencil_cache)
+ mask |= GL_STENCIL_BUFFER_BIT;
+
+ /* The last time we drew to the surface, we were using multisampling,
+ so we need to blit from the multisampling framebuffer into the
+ non-multisampling framebuffer. */
+#if CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) {
+ ctx->dispatch.BindFramebuffer (GL_DRAW_FRAMEBUFFER_ANGLE, surface->fb);
+ ctx->dispatch.BindFramebuffer (GL_READ_FRAMEBUFFER_ANGLE, surface->msaa_fb);
+ }
+#if CAIRO_HAS_EVASGL_SURFACE
+ else {
+ ctx->dispatch.BindFramebuffer (GL_DRAW_FRAMEBUFFER, surface->fb);
+ ctx->dispatch.BindFramebuffer (GL_READ_FRAMEBUFFER, surface->msaa_fb);
+ }
+#endif
+#else
+ ctx->dispatch.BindFramebuffer (GL_DRAW_FRAMEBUFFER, surface->fb);
+ ctx->dispatch.BindFramebuffer (GL_READ_FRAMEBUFFER, surface->msaa_fb);
+#endif
+ ctx->dispatch.BlitFramebuffer (0, 0, surface->width, surface->height,
+ 0, 0, surface->width, surface->height,
+ mask, GL_NEAREST);
+ ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb);
+
+ surface->content_synced = TRUE;
+ /* re-enable stencil and scissor test */
+ if (scissor_test_enabled)
+ _enable_scissor_buffer (ctx);
+ if (stencil_test_enabled)
+ _enable_stencil_buffer (ctx);
+}
+
+void
+_cairo_gl_context_bind_framebuffer (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface,
+ cairo_bool_t multisampling)
+{
+ if (_cairo_gl_surface_is_texture (surface)) {
+ /* OpenGL ES surfaces only have either a multisample framebuffer or a
+ * singlesample framebuffer, so we cannot switch back and forth. */
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ ! ctx->has_angle_multisampling) {
+ _cairo_gl_ensure_framebuffer (ctx, surface);
+ ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb);
+ _cairo_gl_clear_framebuffer (ctx, surface);
+ return;
+ }
+
+ if (multisampling)
+ bind_multisample_framebuffer (ctx, surface);
+ else
+ bind_singlesample_framebuffer (ctx, surface);
+ } else {
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_GLESV3_SURFACE
+ ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, 0);
+#endif
+
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) {
+ if (multisampling)
+ ctx->dispatch.Enable (GL_MULTISAMPLE);
+ else
+ ctx->dispatch.Disable (GL_MULTISAMPLE);
+ }
+#endif
+ }
+
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP ||
+ ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 ||
+ (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ ctx->has_angle_multisampling))
+ surface->msaa_active = multisampling;
+
+ if (ctx->gl_flavor != CAIRO_GL_FLAVOR_DESKTOP && multisampling)
+ _cairo_gl_clear_framebuffer (ctx, surface);
+}
+
+void
+_cairo_gl_context_set_destination (cairo_gl_context_t *ctx,
+ cairo_gl_surface_t *surface,
+ cairo_bool_t multisampling)
+{
+ cairo_bool_t changing_surface, changing_sampling;
+
+ /* The decision whether or not to use multisampling happens when
+ * we create an OpenGL ES surface, so we can never switch modes. */
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 &&
+ ! ctx->has_angle_multisampling)
+ multisampling = surface->msaa_active;
+
+ changing_surface = ctx->current_target != surface || surface->size_changed;
+ changing_sampling = surface->msaa_active != multisampling;
+
+ if (! changing_surface && ! changing_sampling) {
+ if (surface->needs_update)
+ _cairo_gl_composite_flush (ctx);
+ return;
+ }
+ if (! changing_surface) {
+ _cairo_gl_composite_flush (ctx);
+ _cairo_gl_context_bind_framebuffer (ctx, surface, multisampling);
+ return;
+ }
+
+ _cairo_gl_composite_flush (ctx);
+
+ ctx->current_target = surface;
+ surface->needs_update = FALSE;
+
+ if (! _cairo_gl_surface_is_texture (surface)) {
+ ctx->make_current (ctx, surface);
+ }
+
+ _cairo_gl_context_bind_framebuffer (ctx, surface, multisampling);
+
+ if (! _cairo_gl_surface_is_texture (surface)) {
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_EVASGL_SURFACE
+ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP &&
+ ctx->dispatch.DrawBuffer &&
+ ctx->dispatch.ReadBuffer) {
+ ctx->dispatch.DrawBuffer (GL_BACK_LEFT);
+ ctx->dispatch.ReadBuffer (GL_BACK_LEFT);
+ }
+#endif
+ }
+
+ ctx->dispatch.Disable (GL_DITHER);
+ if (ctx->states_cache.viewport_box.width != surface->width ||
+ ctx->states_cache.viewport_box.height != surface->height) {
+ ctx->dispatch.Viewport (0, 0, surface->width, surface->height);
+ ctx->states_cache.viewport_box.width = surface->width;
+ ctx->states_cache.viewport_box.height = surface->height;
+ }
+
+ if (_cairo_gl_surface_is_texture (surface))
+ _gl_identity_ortho (ctx->modelviewprojection_matrix,
+ 0, surface->width, 0, surface->height);
+ else
+ _gl_identity_ortho (ctx->modelviewprojection_matrix,
+ 0, surface->width, surface->height, 0);
+}
+
+void
+cairo_gl_device_set_thread_aware (cairo_device_t *device,
+ cairo_bool_t thread_aware)
+{
+ if ((! device)||(cairo_device_status(device)!= CAIRO_STATUS_SUCCESS)) {
+ fprintf (stderr, "cairo_gl_device_set_thread_aware(): cairo_device is NULL or not available\n");
+ _cairo_error_throw (CAIRO_STATUS_DEVICE_ERROR);
+ return;
+ }
+ if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
+ _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+ return;
+ }
+ if(thread_aware == 0 || thread_aware == 1){
+ ((cairo_gl_context_t *) device)->thread_aware = thread_aware;
+ }
+ else{
+ _cairo_device_set_error (device, CAIRO_STATUS_INVALID_STATUS);
+ return;
+ }
+}
+
+void _cairo_gl_context_reset (cairo_gl_context_t *ctx)
+{
+ ctx->states_cache.viewport_box.width = 0;
+ ctx->states_cache.viewport_box.height = 0;
+
+ ctx->states_cache.clear_red = -1;
+ ctx->states_cache.clear_green = -1;
+ ctx->states_cache.clear_blue = -1;
+ ctx->states_cache.clear_alpha = -1;
+
+ ctx->states_cache.blend_enabled = FALSE;
+
+ ctx->states_cache.src_color_factor = CAIRO_GL_ENUM_UNINITIALIZED;
+ ctx->states_cache.dst_color_factor = CAIRO_GL_ENUM_UNINITIALIZED;
+ ctx->states_cache.src_alpha_factor = CAIRO_GL_ENUM_UNINITIALIZED;
+ ctx->states_cache.dst_alpha_factor = CAIRO_GL_ENUM_UNINITIALIZED;
+
+ ctx->states_cache.active_texture = CAIRO_GL_ENUM_UNINITIALIZED;
+
+ ctx->states_cache.depth_mask = FALSE;
+
+ /* FIXME: this is hack to fix mali driver*/
+ ctx->dispatch.Disable (GL_DITHER);
+
+ ctx->current_shader = NULL;
+}