diff options
Diffstat (limited to 'src/cairo-gl-device.c')
-rwxr-xr-x | src/cairo-gl-device.c | 1092 |
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; +} |