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,2010 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  *	Benjamin Otte <otte@gnome.org>
36  *	Carl Worth <cworth@cworth.org>
37  *	Chris Wilson <chris@chris-wilson.co.uk>
38  *	Eric Anholt <eric@anholt.net>
39  */
40 
41 #include "cairoint.h"
42 
43 #include "cairo-error-private.h"
44 #include "cairo-gl-private.h"
45 
46 static void
_gl_lock(void * device)47 _gl_lock (void *device)
48 {
49     cairo_gl_context_t *ctx = (cairo_gl_context_t *) device;
50 
51     ctx->acquire (ctx);
52 }
53 
54 static void
_gl_unlock(void * device)55 _gl_unlock (void *device)
56 {
57     cairo_gl_context_t *ctx = (cairo_gl_context_t *) device;
58 
59     ctx->release (ctx);
60 }
61 
62 static cairo_status_t
_gl_flush(void * device)63 _gl_flush (void *device)
64 {
65     cairo_gl_context_t *ctx;
66     cairo_status_t status;
67 
68     status = _cairo_gl_context_acquire (device, &ctx);
69     if (unlikely (status))
70         return status;
71 
72     _cairo_gl_composite_flush (ctx);
73 
74     _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE);
75     _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK);
76 
77     if (ctx->clip_region) {
78         cairo_region_destroy (ctx->clip_region);
79         ctx->clip_region = NULL;
80     }
81 
82     ctx->current_target = NULL;
83     ctx->current_operator = -1;
84     ctx->vertex_size = 0;
85     ctx->pre_shader = NULL;
86     _cairo_gl_set_shader (ctx, NULL);
87 
88     glBindBufferARB (GL_ARRAY_BUFFER_ARB, 0);
89 
90     glDisableClientState (GL_VERTEX_ARRAY);
91 
92     glDisable (GL_SCISSOR_TEST);
93     glDisable (GL_BLEND);
94 
95     return _cairo_gl_context_release (ctx, status);
96 }
97 
98 static void
_gl_finish(void * device)99 _gl_finish (void *device)
100 {
101     cairo_gl_context_t *ctx = device;
102 
103     _gl_lock (device);
104 
105     _cairo_cache_fini (&ctx->gradients);
106 
107     _cairo_gl_context_fini_shaders (ctx);
108 
109     _gl_unlock (device);
110 }
111 
112 static void
_gl_destroy(void * device)113 _gl_destroy (void *device)
114 {
115     cairo_gl_context_t *ctx = device;
116     cairo_scaled_font_t *scaled_font, *next_scaled_font;
117     int n;
118 
119     ctx->acquire (ctx);
120 
121     cairo_list_foreach_entry_safe (scaled_font,
122 				   next_scaled_font,
123 				   cairo_scaled_font_t,
124 				   &ctx->fonts,
125 				   link)
126     {
127 	_cairo_scaled_font_revoke_ownership (scaled_font);
128     }
129 
130     for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
131 	_cairo_gl_glyph_cache_fini (ctx, &ctx->glyph_cache[n]);
132 
133     cairo_region_destroy (ctx->clip_region);
134 
135     ctx->destroy (ctx);
136 
137     free (ctx);
138 }
139 
140 static const cairo_device_backend_t _cairo_gl_device_backend = {
141     CAIRO_DEVICE_TYPE_GL,
142 
143     _gl_lock,
144     _gl_unlock,
145 
146     _gl_flush, /* flush */
147     _gl_finish,
148     _gl_destroy,
149 };
150 
151 cairo_status_t
_cairo_gl_context_init(cairo_gl_context_t * ctx)152 _cairo_gl_context_init (cairo_gl_context_t *ctx)
153 {
154     cairo_status_t status;
155     int n;
156 
157     _cairo_device_init (&ctx->base, &_cairo_gl_device_backend);
158 
159     memset (ctx->glyph_cache, 0, sizeof (ctx->glyph_cache));
160     cairo_list_init (&ctx->fonts);
161 
162     if (glewInit () != GLEW_OK)
163 	return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */
164 
165     if (! GLEW_EXT_framebuffer_object ||
166 	! GLEW_ARB_texture_env_combine ||
167 	! GLEW_EXT_bgra)
168     {
169 	fprintf (stderr,
170 		 "Required GL extensions not available:\n");
171 	if (! GLEW_EXT_framebuffer_object)
172 	    fprintf (stderr, "    GL_EXT_framebuffer_object\n");
173 	if (! GLEW_ARB_texture_env_combine)
174 	    fprintf (stderr, "    GL_ARB_texture_env_combine\n");
175         if (! GLEW_ARB_vertex_buffer_object)
176 	    fprintf (stderr, "    GL_ARB_vertex_buffer_object\n");
177 
178 	/* EXT_bgra is used in two places:
179 	 * - draw_image to upload common pixman formats without hand-swizzling.
180 	 * - get_image to download common pixman formats without hand-swizzling.
181 	 */
182 	if (! GLEW_EXT_bgra)
183 	    fprintf (stderr, "    GL_EXT_bgra\n");
184 
185 	return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */
186     }
187 
188     if (! GLEW_ARB_texture_non_power_of_two &&
189 	! GLEW_ARB_texture_rectangle ) {
190 	fprintf (stderr,
191 		 "Required GL extensions not available:\n");
192 	fprintf (stderr, "    GL_ARB_texture_non_power_of_two, GL_ARB_texture_rectangle\n");
193     }
194 
195     if (! GLEW_ARB_texture_non_power_of_two)
196 	ctx->tex_target = GL_TEXTURE_RECTANGLE_EXT;
197     else
198 	ctx->tex_target = GL_TEXTURE_2D;
199 
200     ctx->current_operator = -1;
201 
202     status = _cairo_gl_context_init_shaders (ctx);
203     if (unlikely (status))
204         return status;
205 
206     status = _cairo_cache_init (&ctx->gradients,
207                                 _cairo_gl_gradient_equal,
208                                 NULL,
209                                 (cairo_destroy_func_t) _cairo_gl_gradient_destroy,
210                                 CAIRO_GL_GRADIENT_CACHE_SIZE);
211     if (unlikely (status))
212         return status;
213 
214     /* Set up the dummy texture for tex_env_combine with constant color. */
215     glGenTextures (1, &ctx->dummy_tex);
216     glBindTexture (ctx->tex_target, ctx->dummy_tex);
217     glTexImage2D (ctx->tex_target, 0, GL_RGBA, 1, 1, 0,
218 		  GL_RGBA, GL_UNSIGNED_BYTE, NULL);
219 
220     /* PBO for any sort of texture upload */
221     glGenBuffersARB (1, &ctx->texture_load_pbo);
222     glGenBuffersARB (1, &ctx->vbo);
223 
224     ctx->max_framebuffer_size = 0;
225     glGetIntegerv (GL_MAX_RENDERBUFFER_SIZE, &ctx->max_framebuffer_size);
226     ctx->max_texture_size = 0;
227     glGetIntegerv (GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size);
228     ctx->max_textures = 0;
229     glGetIntegerv (GL_MAX_TEXTURE_UNITS, &ctx->max_textures);
230 
231     for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
232 	_cairo_gl_glyph_cache_init (&ctx->glyph_cache[n]);
233 
234     return CAIRO_STATUS_SUCCESS;
235 }
236 
237 void
_cairo_gl_context_activate(cairo_gl_context_t * ctx,cairo_gl_tex_t tex_unit)238 _cairo_gl_context_activate (cairo_gl_context_t *ctx,
239                             cairo_gl_tex_t      tex_unit)
240 {
241     if (ctx->max_textures <= (GLint) tex_unit) {
242         if (tex_unit < 2) {
243             _cairo_gl_composite_flush (ctx);
244             _cairo_gl_context_destroy_operand (ctx, ctx->max_textures - 1);
245         }
246         glActiveTexture (ctx->max_textures - 1);
247     } else {
248         glActiveTexture (GL_TEXTURE0 + tex_unit);
249     }
250 }
251 
252 static void
_cairo_gl_ensure_framebuffer(cairo_gl_context_t * ctx,cairo_gl_surface_t * surface)253 _cairo_gl_ensure_framebuffer (cairo_gl_context_t *ctx,
254                               cairo_gl_surface_t *surface)
255 {
256     GLenum status;
257 
258     if (likely (surface->fb))
259         return;
260 
261     /* Create a framebuffer object wrapping the texture so that we can render
262      * to it.
263      */
264     glGenFramebuffersEXT (1, &surface->fb);
265     glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, surface->fb);
266     glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT,
267 			       GL_COLOR_ATTACHMENT0_EXT,
268 			       ctx->tex_target,
269 			       surface->tex,
270 			       0);
271 
272     status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
273     if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
274 	const char *str;
275 	switch (status) {
276 	//case GL_FRAMEBUFFER_UNDEFINED_EXT: str= "undefined"; break;
277 	case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: str= "incomplete attachment"; break;
278 	case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: str= "incomplete/missing attachment"; break;
279 	case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: str= "incomplete draw buffer"; break;
280 	case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: str= "incomplete read buffer"; break;
281 	case GL_FRAMEBUFFER_UNSUPPORTED_EXT: str= "unsupported"; break;
282 	case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT: str= "incomplete multiple"; break;
283 	default: str = "unknown error"; break;
284 	}
285 
286 	fprintf (stderr,
287 		 "destination is framebuffer incomplete: %s [%#x]\n",
288 		 str, status);
289     }
290 }
291 
292 void
_cairo_gl_context_set_destination(cairo_gl_context_t * ctx,cairo_gl_surface_t * surface)293 _cairo_gl_context_set_destination (cairo_gl_context_t *ctx,
294                                    cairo_gl_surface_t *surface)
295 {
296     if (ctx->current_target == surface)
297         return;
298 
299     _cairo_gl_composite_flush (ctx);
300 
301     ctx->current_target = surface;
302 
303     if (_cairo_gl_surface_is_texture (surface)) {
304         _cairo_gl_ensure_framebuffer (ctx, surface);
305         glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, surface->fb);
306         glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
307         glReadBuffer (GL_COLOR_ATTACHMENT0_EXT);
308     } else {
309         ctx->make_current (ctx, surface);
310         glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
311         glDrawBuffer (GL_BACK_LEFT);
312         glReadBuffer (GL_BACK_LEFT);
313     }
314 
315     glViewport (0, 0, surface->width, surface->height);
316 
317     glMatrixMode (GL_PROJECTION);
318     glLoadIdentity ();
319     if (_cairo_gl_surface_is_texture (surface))
320 	glOrtho (0, surface->width, 0, surface->height, -1.0, 1.0);
321     else
322 	glOrtho (0, surface->width, surface->height, 0, -1.0, 1.0);
323 
324     glMatrixMode (GL_MODELVIEW);
325     glLoadIdentity ();
326 }
327