beta-0.89.2
[luatex.git] / source / libs / cairo / cairo-src / src / cairo-gl-shaders.c
blobfe975d2d7c9fb5a07d10dc848f76b9ca4a34e015
1 /* cairo - a vector graphics library with display and print output
3 * Copyright © 2009 T. Zachary Laine
4 * Copyright © 2010 Eric Anholt
5 * Copyright © 2010 Red Hat, Inc
6 * Copyright © 2010 Linaro Limited
8 * This library is free software; you can redistribute it and/or
9 * modify it either under the terms of the GNU Lesser General Public
10 * License version 2.1 as published by the Free Software Foundation
11 * (the "LGPL") or, at your option, under the terms of the Mozilla
12 * Public License Version 1.1 (the "MPL"). If you do not alter this
13 * notice, a recipient may use your version of this file under either
14 * the MPL or the LGPL.
16 * You should have received a copy of the LGPL along with this library
17 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
19 * You should have received a copy of the MPL along with this library
20 * in the file COPYING-MPL-1.1
22 * The contents of this file are subject to the Mozilla Public License
23 * Version 1.1 (the "License"); you may not use this file except in
24 * compliance with the License. You may obtain a copy of the License at
25 * http://www.mozilla.org/MPL/
27 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
28 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
29 * the specific language governing rights and limitations.
31 * The Original Code is the cairo graphics library.
33 * The Initial Developer of the Original Code is T. Zachary Laine.
35 * Contributor(s):
36 * Benjamin Otte <otte@gnome.org>
37 * Eric Anholt <eric@anholt.net>
38 * T. Zachary Laine <whatwasthataddress@gmail.com>
39 * Alexandros Frantzis <alexandros.frantzis@linaro.org>
42 #include "cairoint.h"
43 #include "cairo-gl-private.h"
44 #include "cairo-error-private.h"
45 #include "cairo-output-stream-private.h"
47 static cairo_status_t
48 _cairo_gl_shader_compile_and_link (cairo_gl_context_t *ctx,
49 cairo_gl_shader_t *shader,
50 cairo_gl_var_type_t src,
51 cairo_gl_var_type_t mask,
52 cairo_bool_t use_coverage,
53 const char *fragment_text);
55 typedef struct _cairo_shader_cache_entry {
56 cairo_cache_entry_t base;
58 unsigned vertex;
60 cairo_gl_operand_type_t src;
61 cairo_gl_operand_type_t mask;
62 cairo_gl_operand_type_t dest;
63 cairo_bool_t use_coverage;
65 cairo_gl_shader_in_t in;
66 GLint src_gl_filter;
67 cairo_bool_t src_border_fade;
68 cairo_extend_t src_extend;
69 GLint mask_gl_filter;
70 cairo_bool_t mask_border_fade;
71 cairo_extend_t mask_extend;
73 cairo_gl_context_t *ctx; /* XXX: needed to destroy the program */
74 cairo_gl_shader_t shader;
75 } cairo_shader_cache_entry_t;
77 static cairo_bool_t
78 _cairo_gl_shader_cache_equal_desktop (const void *key_a, const void *key_b)
80 const cairo_shader_cache_entry_t *a = key_a;
81 const cairo_shader_cache_entry_t *b = key_b;
82 cairo_bool_t both_have_npot_repeat =
83 a->ctx->has_npot_repeat && b->ctx->has_npot_repeat;
85 return (a->vertex == b->vertex &&
86 a->src == b->src &&
87 a->mask == b->mask &&
88 a->dest == b->dest &&
89 a->use_coverage == b->use_coverage &&
90 a->in == b->in &&
91 (both_have_npot_repeat || a->src_extend == b->src_extend) &&
92 (both_have_npot_repeat || a->mask_extend == b->mask_extend));
96 * For GLES2 we use more complicated shaders to implement missing GL
97 * features. In this case we need more parameters to uniquely identify
98 * a shader (vs _cairo_gl_shader_cache_equal_desktop()).
100 static cairo_bool_t
101 _cairo_gl_shader_cache_equal_gles2 (const void *key_a, const void *key_b)
103 const cairo_shader_cache_entry_t *a = key_a;
104 const cairo_shader_cache_entry_t *b = key_b;
105 cairo_bool_t both_have_npot_repeat =
106 a->ctx->has_npot_repeat && b->ctx->has_npot_repeat;
108 return (a->vertex == b->vertex &&
109 a->src == b->src &&
110 a->mask == b->mask &&
111 a->dest == b->dest &&
112 a->use_coverage == b->use_coverage &&
113 a->in == b->in &&
114 a->src_gl_filter == b->src_gl_filter &&
115 a->src_border_fade == b->src_border_fade &&
116 (both_have_npot_repeat || a->src_extend == b->src_extend) &&
117 a->mask_gl_filter == b->mask_gl_filter &&
118 a->mask_border_fade == b->mask_border_fade &&
119 (both_have_npot_repeat || a->mask_extend == b->mask_extend));
122 static unsigned long
123 _cairo_gl_shader_cache_hash (const cairo_shader_cache_entry_t *entry)
125 return ((entry->src << 24) | (entry->mask << 16) | (entry->dest << 8) | (entry->in << 1) | entry->use_coverage) ^ entry->vertex;
128 static void
129 _cairo_gl_shader_cache_destroy (void *data)
131 cairo_shader_cache_entry_t *entry = data;
133 _cairo_gl_shader_fini (entry->ctx, &entry->shader);
134 if (entry->ctx->current_shader == &entry->shader)
135 entry->ctx->current_shader = NULL;
136 free (entry);
139 static void
140 _cairo_gl_shader_init (cairo_gl_shader_t *shader)
142 shader->fragment_shader = 0;
143 shader->program = 0;
146 cairo_status_t
147 _cairo_gl_context_init_shaders (cairo_gl_context_t *ctx)
149 static const char *fill_fs_source =
150 "#ifdef GL_ES\n"
151 "precision mediump float;\n"
152 "#endif\n"
153 "uniform vec4 color;\n"
154 "void main()\n"
155 "{\n"
156 " gl_FragColor = color;\n"
157 "}\n";
158 cairo_status_t status;
160 if (_cairo_gl_get_version () >= CAIRO_GL_VERSION_ENCODE (2, 0) ||
161 (_cairo_gl_has_extension ("GL_ARB_shader_objects") &&
162 _cairo_gl_has_extension ("GL_ARB_fragment_shader") &&
163 _cairo_gl_has_extension ("GL_ARB_vertex_shader"))) {
164 ctx->has_shader_support = TRUE;
165 } else {
166 ctx->has_shader_support = FALSE;
167 fprintf (stderr, "Error: The cairo gl backend requires shader support!\n");
168 return CAIRO_STATUS_DEVICE_ERROR;
171 memset (ctx->vertex_shaders, 0, sizeof (ctx->vertex_shaders));
173 status = _cairo_cache_init (&ctx->shaders,
174 ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP ?
175 _cairo_gl_shader_cache_equal_desktop :
176 _cairo_gl_shader_cache_equal_gles2,
177 NULL,
178 _cairo_gl_shader_cache_destroy,
179 CAIRO_GL_MAX_SHADERS_PER_CONTEXT);
180 if (unlikely (status))
181 return status;
183 _cairo_gl_shader_init (&ctx->fill_rectangles_shader);
184 status = _cairo_gl_shader_compile_and_link (ctx,
185 &ctx->fill_rectangles_shader,
186 CAIRO_GL_VAR_NONE,
187 CAIRO_GL_VAR_NONE,
188 FALSE,
189 fill_fs_source);
190 if (unlikely (status))
191 return status;
193 return CAIRO_STATUS_SUCCESS;
196 void
197 _cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx)
199 int i;
201 for (i = 0; i < CAIRO_GL_VAR_TYPE_MAX; i++) {
202 if (ctx->vertex_shaders[i])
203 ctx->dispatch.DeleteShader (ctx->vertex_shaders[i]);
206 _cairo_cache_fini (&ctx->shaders);
209 void
210 _cairo_gl_shader_fini (cairo_gl_context_t *ctx,
211 cairo_gl_shader_t *shader)
213 if (shader->fragment_shader)
214 ctx->dispatch.DeleteShader (shader->fragment_shader);
216 if (shader->program)
217 ctx->dispatch.DeleteProgram (shader->program);
220 static const char *operand_names[] = { "source", "mask", "dest" };
222 static cairo_gl_var_type_t
223 cairo_gl_operand_get_var_type (cairo_gl_operand_t *operand)
225 switch (operand->type) {
226 default:
227 case CAIRO_GL_OPERAND_COUNT:
228 ASSERT_NOT_REACHED;
229 case CAIRO_GL_OPERAND_NONE:
230 case CAIRO_GL_OPERAND_CONSTANT:
231 return CAIRO_GL_VAR_NONE;
232 case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
233 case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
234 case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
235 case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
236 return operand->gradient.texgen ? CAIRO_GL_VAR_TEXGEN : CAIRO_GL_VAR_TEXCOORDS;
237 case CAIRO_GL_OPERAND_TEXTURE:
238 return operand->texture.texgen ? CAIRO_GL_VAR_TEXGEN : CAIRO_GL_VAR_TEXCOORDS;
242 static void
243 cairo_gl_shader_emit_variable (cairo_output_stream_t *stream,
244 cairo_gl_var_type_t type,
245 cairo_gl_tex_t name)
247 switch (type) {
248 default:
249 ASSERT_NOT_REACHED;
250 case CAIRO_GL_VAR_NONE:
251 break;
252 case CAIRO_GL_VAR_TEXCOORDS:
253 _cairo_output_stream_printf (stream,
254 "attribute vec4 MultiTexCoord%d;\n"
255 "varying vec2 %s_texcoords;\n",
256 name,
257 operand_names[name]);
258 break;
259 case CAIRO_GL_VAR_TEXGEN:
260 _cairo_output_stream_printf (stream,
261 "uniform mat3 %s_texgen;\n"
262 "varying vec2 %s_texcoords;\n",
263 operand_names[name],
264 operand_names[name]);
265 break;
269 static void
270 cairo_gl_shader_emit_vertex (cairo_output_stream_t *stream,
271 cairo_gl_var_type_t type,
272 cairo_gl_tex_t name)
274 switch (type) {
275 default:
276 ASSERT_NOT_REACHED;
277 case CAIRO_GL_VAR_NONE:
278 break;
279 case CAIRO_GL_VAR_TEXCOORDS:
280 _cairo_output_stream_printf (stream,
281 " %s_texcoords = MultiTexCoord%d.xy;\n",
282 operand_names[name], name);
283 break;
285 case CAIRO_GL_VAR_TEXGEN:
286 _cairo_output_stream_printf (stream,
287 " %s_texcoords = (%s_texgen * Vertex.xyw).xy;\n",
288 operand_names[name], operand_names[name]);
289 break;
293 static void
294 cairo_gl_shader_dcl_coverage (cairo_output_stream_t *stream)
296 _cairo_output_stream_printf (stream, "varying float coverage;\n");
299 static void
300 cairo_gl_shader_def_coverage (cairo_output_stream_t *stream)
302 _cairo_output_stream_printf (stream, " coverage = Color.a;\n");
305 static cairo_status_t
306 cairo_gl_shader_get_vertex_source (cairo_gl_var_type_t src,
307 cairo_gl_var_type_t mask,
308 cairo_bool_t use_coverage,
309 cairo_gl_var_type_t dest,
310 char **out)
312 cairo_output_stream_t *stream = _cairo_memory_stream_create ();
313 unsigned char *source;
314 unsigned long length;
315 cairo_status_t status;
317 cairo_gl_shader_emit_variable (stream, src, CAIRO_GL_TEX_SOURCE);
318 cairo_gl_shader_emit_variable (stream, mask, CAIRO_GL_TEX_MASK);
319 if (use_coverage)
320 cairo_gl_shader_dcl_coverage (stream);
322 _cairo_output_stream_printf (stream,
323 "attribute vec4 Vertex;\n"
324 "attribute vec4 Color;\n"
325 "uniform mat4 ModelViewProjectionMatrix;\n"
326 "void main()\n"
327 "{\n"
328 " gl_Position = ModelViewProjectionMatrix * Vertex;\n");
330 cairo_gl_shader_emit_vertex (stream, src, CAIRO_GL_TEX_SOURCE);
331 cairo_gl_shader_emit_vertex (stream, mask, CAIRO_GL_TEX_MASK);
332 if (use_coverage)
333 cairo_gl_shader_def_coverage (stream);
335 _cairo_output_stream_write (stream,
336 "}\n\0", 3);
338 status = _cairo_memory_stream_destroy (stream, &source, &length);
339 if (unlikely (status))
340 return status;
342 *out = (char *) source;
343 return CAIRO_STATUS_SUCCESS;
347 * Returns whether an operand needs a special border fade fragment shader
348 * to simulate the GL_CLAMP_TO_BORDER wrapping method that is missing in GLES2.
350 static cairo_bool_t
351 _cairo_gl_shader_needs_border_fade (cairo_gl_operand_t *operand)
353 cairo_extend_t extend =_cairo_gl_operand_get_extend (operand);
355 return extend == CAIRO_EXTEND_NONE &&
356 (operand->type == CAIRO_GL_OPERAND_TEXTURE ||
357 operand->type == CAIRO_GL_OPERAND_LINEAR_GRADIENT ||
358 operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE ||
359 operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0);
362 static void
363 cairo_gl_shader_emit_color (cairo_output_stream_t *stream,
364 cairo_gl_context_t *ctx,
365 cairo_gl_operand_t *op,
366 cairo_gl_tex_t name)
368 const char *namestr = operand_names[name];
369 const char *rectstr = (ctx->tex_target == GL_TEXTURE_RECTANGLE ? "Rect" : "");
371 switch (op->type) {
372 case CAIRO_GL_OPERAND_COUNT:
373 default:
374 ASSERT_NOT_REACHED;
375 break;
376 case CAIRO_GL_OPERAND_NONE:
377 _cairo_output_stream_printf (stream,
378 "vec4 get_%s()\n"
379 "{\n"
380 " return vec4 (0, 0, 0, 1);\n"
381 "}\n",
382 namestr);
383 break;
384 case CAIRO_GL_OPERAND_CONSTANT:
385 _cairo_output_stream_printf (stream,
386 "uniform vec4 %s_constant;\n"
387 "vec4 get_%s()\n"
388 "{\n"
389 " return %s_constant;\n"
390 "}\n",
391 namestr, namestr, namestr);
392 break;
393 case CAIRO_GL_OPERAND_TEXTURE:
394 _cairo_output_stream_printf (stream,
395 "uniform sampler2D%s %s_sampler;\n"
396 "uniform vec2 %s_texdims;\n"
397 "varying vec2 %s_texcoords;\n"
398 "vec4 get_%s()\n"
399 "{\n",
400 rectstr, namestr, namestr, namestr, namestr);
401 if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
402 _cairo_gl_shader_needs_border_fade (op))
404 _cairo_output_stream_printf (stream,
405 " vec2 border_fade = %s_border_fade (%s_texcoords, %s_texdims);\n"
406 " vec4 texel = texture2D%s (%s_sampler, %s_texcoords);\n"
407 " return texel * border_fade.x * border_fade.y;\n"
408 "}\n",
409 namestr, namestr, namestr, rectstr, namestr, namestr);
411 else
413 _cairo_output_stream_printf (stream,
414 " return texture2D%s (%s_sampler, %s_wrap (%s_texcoords));\n"
415 "}\n",
416 rectstr, namestr, namestr, namestr);
418 break;
419 case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
420 _cairo_output_stream_printf (stream,
421 "varying vec2 %s_texcoords;\n"
422 "uniform vec2 %s_texdims;\n"
423 "uniform sampler2D%s %s_sampler;\n"
424 "\n"
425 "vec4 get_%s()\n"
426 "{\n",
427 namestr, namestr, rectstr, namestr, namestr);
428 if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
429 _cairo_gl_shader_needs_border_fade (op))
431 _cairo_output_stream_printf (stream,
432 " float border_fade = %s_border_fade (%s_texcoords.x, %s_texdims.x);\n"
433 " vec4 texel = texture2D%s (%s_sampler, vec2 (%s_texcoords.x, 0.5));\n"
434 " return texel * border_fade;\n"
435 "}\n",
436 namestr, namestr, namestr, rectstr, namestr, namestr);
438 else
440 _cairo_output_stream_printf (stream,
441 " return texture2D%s (%s_sampler, %s_wrap (vec2 (%s_texcoords.x, 0.5)));\n"
442 "}\n",
443 rectstr, namestr, namestr, namestr);
445 break;
446 case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
447 _cairo_output_stream_printf (stream,
448 "varying vec2 %s_texcoords;\n"
449 "uniform vec2 %s_texdims;\n"
450 "uniform sampler2D%s %s_sampler;\n"
451 "uniform vec3 %s_circle_d;\n"
452 "uniform float %s_radius_0;\n"
453 "\n"
454 "vec4 get_%s()\n"
455 "{\n"
456 " vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
457 " \n"
458 " float B = dot (pos, %s_circle_d);\n"
459 " float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
460 " \n"
461 " float t = 0.5 * C / B;\n"
462 " float is_valid = step (-%s_radius_0, t * %s_circle_d.z);\n",
463 namestr, namestr, rectstr, namestr, namestr, namestr, namestr,
464 namestr, namestr, namestr, namestr, namestr);
465 if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
466 _cairo_gl_shader_needs_border_fade (op))
468 _cairo_output_stream_printf (stream,
469 " float border_fade = %s_border_fade (t, %s_texdims.x);\n"
470 " vec4 texel = texture2D%s (%s_sampler, vec2 (t, 0.5));\n"
471 " return mix (vec4 (0.0), texel * border_fade, is_valid);\n"
472 "}\n",
473 namestr, namestr, rectstr, namestr);
475 else
477 _cairo_output_stream_printf (stream,
478 " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2 (t, 0.5)));\n"
479 " return mix (vec4 (0.0), texel, is_valid);\n"
480 "}\n",
481 rectstr, namestr, namestr);
483 break;
484 case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
485 _cairo_output_stream_printf (stream,
486 "varying vec2 %s_texcoords;\n"
487 "uniform vec2 %s_texdims;\n"
488 "uniform sampler2D%s %s_sampler;\n"
489 "uniform vec3 %s_circle_d;\n"
490 "uniform float %s_a;\n"
491 "uniform float %s_radius_0;\n"
492 "\n"
493 "vec4 get_%s()\n"
494 "{\n"
495 " vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
496 " \n"
497 " float B = dot (pos, %s_circle_d);\n"
498 " float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
499 " \n"
500 " float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n"
501 " float sqrtdet = sqrt (abs (det));\n"
502 " vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n"
503 " \n"
504 " vec2 is_valid = step (vec2 (0.0), t) * step (t, vec2(1.0));\n"
505 " float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n"
506 " \n"
507 " float upper_t = mix (t.y, t.x, is_valid.x);\n",
508 namestr, namestr, rectstr, namestr, namestr, namestr, namestr,
509 namestr, namestr, namestr, namestr, namestr, namestr);
510 if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
511 _cairo_gl_shader_needs_border_fade (op))
513 _cairo_output_stream_printf (stream,
514 " float border_fade = %s_border_fade (upper_t, %s_texdims.x);\n"
515 " vec4 texel = texture2D%s (%s_sampler, vec2 (upper_t, 0.5));\n"
516 " return mix (vec4 (0.0), texel * border_fade, has_color);\n"
517 "}\n",
518 namestr, namestr, rectstr, namestr);
520 else
522 _cairo_output_stream_printf (stream,
523 " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n"
524 " return mix (vec4 (0.0), texel, has_color);\n"
525 "}\n",
526 rectstr, namestr, namestr);
528 break;
529 case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
530 _cairo_output_stream_printf (stream,
531 "varying vec2 %s_texcoords;\n"
532 "uniform sampler2D%s %s_sampler;\n"
533 "uniform vec3 %s_circle_d;\n"
534 "uniform float %s_a;\n"
535 "uniform float %s_radius_0;\n"
536 "\n"
537 "vec4 get_%s()\n"
538 "{\n"
539 " vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
540 " \n"
541 " float B = dot (pos, %s_circle_d);\n"
542 " float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
543 " \n"
544 " float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n"
545 " float sqrtdet = sqrt (abs (det));\n"
546 " vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n"
547 " \n"
548 " vec2 is_valid = step (vec2 (-%s_radius_0), t * %s_circle_d.z);\n"
549 " float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n"
550 " \n"
551 " float upper_t = mix (t.y, t.x, is_valid.x);\n"
552 " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n"
553 " return mix (vec4 (0.0), texel, has_color);\n"
554 "}\n",
555 namestr, rectstr, namestr, namestr, namestr, namestr,
556 namestr, namestr, namestr, namestr, namestr,
557 namestr, namestr, namestr, rectstr, namestr, namestr);
558 break;
563 * Emits the border fade functions used by an operand.
565 * If bilinear filtering is used, the emitted function performs a linear
566 * fade to transparency effect in the intervals [-1/2n, 1/2n] and
567 * [1 - 1/2n, 1 + 1/2n] (n: texture size).
569 * If nearest filtering is used, the emitted function just returns
570 * 0.0 for all values outside [0, 1).
572 static void
573 _cairo_gl_shader_emit_border_fade (cairo_output_stream_t *stream,
574 cairo_gl_operand_t *operand,
575 cairo_gl_tex_t name)
577 const char *namestr = operand_names[name];
578 GLint gl_filter = _cairo_gl_operand_get_gl_filter (operand);
580 /* 2D version */
581 _cairo_output_stream_printf (stream,
582 "vec2 %s_border_fade (vec2 coords, vec2 dims)\n"
583 "{\n",
584 namestr);
586 if (gl_filter == GL_LINEAR)
587 _cairo_output_stream_printf (stream,
588 " return clamp(-abs(dims * (coords - 0.5)) + (dims + vec2(1.0)) * 0.5, 0.0, 1.0);\n");
589 else
590 _cairo_output_stream_printf (stream,
591 " bvec2 in_tex1 = greaterThanEqual (coords, vec2 (0.0));\n"
592 " bvec2 in_tex2 = lessThan (coords, vec2 (1.0));\n"
593 " return vec2 (float (all (in_tex1) && all (in_tex2)));\n");
595 _cairo_output_stream_printf (stream, "}\n");
597 /* 1D version */
598 _cairo_output_stream_printf (stream,
599 "float %s_border_fade (float x, float dim)\n"
600 "{\n",
601 namestr);
602 if (gl_filter == GL_LINEAR)
603 _cairo_output_stream_printf (stream,
604 " return clamp(-abs(dim * (x - 0.5)) + (dim + 1.0) * 0.5, 0.0, 1.0);\n");
605 else
606 _cairo_output_stream_printf (stream,
607 " bool in_tex = x >= 0.0 && x < 1.0;\n"
608 " return float (in_tex);\n");
610 _cairo_output_stream_printf (stream, "}\n");
614 * Emits the wrap function used by an operand.
616 * In OpenGL ES 2.0, repeat wrap modes (GL_REPEAT and GL_MIRRORED REPEAT) are
617 * only available for NPOT textures if the GL_OES_texture_npot is supported.
618 * If GL_OES_texture_npot is not supported, we need to implement the wrapping
619 * functionality in the shader.
621 static void
622 _cairo_gl_shader_emit_wrap (cairo_gl_context_t *ctx,
623 cairo_output_stream_t *stream,
624 cairo_gl_operand_t *operand,
625 cairo_gl_tex_t name)
627 const char *namestr = operand_names[name];
628 cairo_extend_t extend = _cairo_gl_operand_get_extend (operand);
630 _cairo_output_stream_printf (stream,
631 "vec2 %s_wrap(vec2 coords)\n"
632 "{\n",
633 namestr);
635 if (! ctx->has_npot_repeat &&
636 (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT))
638 if (extend == CAIRO_EXTEND_REPEAT) {
639 _cairo_output_stream_printf (stream,
640 " return fract(coords);\n");
641 } else { /* CAIRO_EXTEND_REFLECT */
642 _cairo_output_stream_printf (stream,
643 " return mix(fract(coords), 1.0 - fract(coords), floor(mod(coords, 2.0)));\n");
646 else
648 _cairo_output_stream_printf (stream, " return coords;\n");
651 _cairo_output_stream_printf (stream, "}\n");
654 static cairo_status_t
655 cairo_gl_shader_get_fragment_source (cairo_gl_context_t *ctx,
656 cairo_gl_shader_in_t in,
657 cairo_gl_operand_t *src,
658 cairo_gl_operand_t *mask,
659 cairo_bool_t use_coverage,
660 cairo_gl_operand_type_t dest_type,
661 char **out)
663 cairo_output_stream_t *stream = _cairo_memory_stream_create ();
664 unsigned char *source;
665 unsigned long length;
666 cairo_status_t status;
667 const char *coverage_str;
669 _cairo_output_stream_printf (stream,
670 "#ifdef GL_ES\n"
671 "precision mediump float;\n"
672 "#endif\n");
674 _cairo_gl_shader_emit_wrap (ctx, stream, src, CAIRO_GL_TEX_SOURCE);
675 _cairo_gl_shader_emit_wrap (ctx, stream, mask, CAIRO_GL_TEX_MASK);
677 if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) {
678 if (_cairo_gl_shader_needs_border_fade (src))
679 _cairo_gl_shader_emit_border_fade (stream, src, CAIRO_GL_TEX_SOURCE);
680 if (_cairo_gl_shader_needs_border_fade (mask))
681 _cairo_gl_shader_emit_border_fade (stream, mask, CAIRO_GL_TEX_MASK);
684 cairo_gl_shader_emit_color (stream, ctx, src, CAIRO_GL_TEX_SOURCE);
685 cairo_gl_shader_emit_color (stream, ctx, mask, CAIRO_GL_TEX_MASK);
687 coverage_str = "";
688 if (use_coverage) {
689 _cairo_output_stream_printf (stream, "varying float coverage;\n");
690 coverage_str = " * coverage";
693 _cairo_output_stream_printf (stream,
694 "void main()\n"
695 "{\n");
696 switch (in) {
697 case CAIRO_GL_SHADER_IN_COUNT:
698 default:
699 ASSERT_NOT_REACHED;
700 case CAIRO_GL_SHADER_IN_NORMAL:
701 _cairo_output_stream_printf (stream,
702 " gl_FragColor = get_source() * get_mask().a%s;\n",
703 coverage_str);
704 break;
705 case CAIRO_GL_SHADER_IN_CA_SOURCE:
706 _cairo_output_stream_printf (stream,
707 " gl_FragColor = get_source() * get_mask()%s;\n",
708 coverage_str);
709 break;
710 case CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA:
711 _cairo_output_stream_printf (stream,
712 " gl_FragColor = get_source().a * get_mask()%s;\n",
713 coverage_str);
714 break;
717 _cairo_output_stream_write (stream,
718 "}\n\0", 3);
720 status = _cairo_memory_stream_destroy (stream, &source, &length);
721 if (unlikely (status))
722 return status;
724 *out = (char *) source;
725 return CAIRO_STATUS_SUCCESS;
728 static void
729 compile_shader (cairo_gl_context_t *ctx,
730 GLuint *shader,
731 GLenum type,
732 const char *source)
734 cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
735 GLint success, log_size, num_chars;
736 char *log;
738 *shader = dispatch->CreateShader (type);
739 dispatch->ShaderSource (*shader, 1, &source, 0);
740 dispatch->CompileShader (*shader);
741 dispatch->GetShaderiv (*shader, GL_COMPILE_STATUS, &success);
743 if (success)
744 return;
746 dispatch->GetShaderiv (*shader, GL_INFO_LOG_LENGTH, &log_size);
747 if (log_size < 0) {
748 printf ("OpenGL shader compilation failed.\n");
749 ASSERT_NOT_REACHED;
750 return;
753 log = _cairo_malloc (log_size + 1);
754 dispatch->GetShaderInfoLog (*shader, log_size, &num_chars, log);
755 log[num_chars] = '\0';
757 printf ("OpenGL shader compilation failed. Shader:\n%s\n", source);
758 printf ("OpenGL compilation log:\n%s\n", log);
760 free (log);
761 ASSERT_NOT_REACHED;
764 static void
765 link_shader_program (cairo_gl_context_t *ctx,
766 GLuint *program,
767 GLuint vert,
768 GLuint frag)
770 cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
771 GLint success, log_size, num_chars;
772 char *log;
774 *program = dispatch->CreateProgram ();
775 dispatch->AttachShader (*program, vert);
776 dispatch->AttachShader (*program, frag);
778 dispatch->BindAttribLocation (*program, CAIRO_GL_VERTEX_ATTRIB_INDEX,
779 "Vertex");
780 dispatch->BindAttribLocation (*program, CAIRO_GL_COLOR_ATTRIB_INDEX,
781 "Color");
782 dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD0_ATTRIB_INDEX,
783 "MultiTexCoord0");
784 dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD1_ATTRIB_INDEX,
785 "MultiTexCoord1");
787 dispatch->LinkProgram (*program);
788 dispatch->GetProgramiv (*program, GL_LINK_STATUS, &success);
789 if (success)
790 return;
792 dispatch->GetProgramiv (*program, GL_INFO_LOG_LENGTH, &log_size);
793 if (log_size < 0) {
794 printf ("OpenGL shader link failed.\n");
795 ASSERT_NOT_REACHED;
796 return;
799 log = _cairo_malloc (log_size + 1);
800 dispatch->GetProgramInfoLog (*program, log_size, &num_chars, log);
801 log[num_chars] = '\0';
803 printf ("OpenGL shader link failed:\n%s\n", log);
804 free (log);
805 ASSERT_NOT_REACHED;
808 static GLint
809 _cairo_gl_get_op_uniform_location(cairo_gl_context_t *ctx,
810 cairo_gl_shader_t *shader,
811 cairo_gl_tex_t tex_unit,
812 const char *suffix)
814 cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
815 char uniform_name[100];
816 const char *unit_name[2] = { "source", "mask" };
818 snprintf (uniform_name, sizeof (uniform_name), "%s_%s",
819 unit_name[tex_unit], suffix);
821 return dispatch->GetUniformLocation (shader->program, uniform_name);
824 static cairo_status_t
825 _cairo_gl_shader_compile_and_link (cairo_gl_context_t *ctx,
826 cairo_gl_shader_t *shader,
827 cairo_gl_var_type_t src,
828 cairo_gl_var_type_t mask,
829 cairo_bool_t use_coverage,
830 const char *fragment_text)
832 cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
833 unsigned int vertex_shader;
834 cairo_status_t status;
835 int i;
837 assert (shader->program == 0);
839 vertex_shader = cairo_gl_var_type_hash (src, mask, use_coverage,
840 CAIRO_GL_VAR_NONE);
841 if (ctx->vertex_shaders[vertex_shader] == 0) {
842 char *source;
844 status = cairo_gl_shader_get_vertex_source (src,
845 mask,
846 use_coverage,
847 CAIRO_GL_VAR_NONE,
848 &source);
849 if (unlikely (status))
850 goto FAILURE;
852 compile_shader (ctx, &ctx->vertex_shaders[vertex_shader],
853 GL_VERTEX_SHADER, source);
854 free (source);
857 compile_shader (ctx, &shader->fragment_shader,
858 GL_FRAGMENT_SHADER, fragment_text);
860 link_shader_program (ctx, &shader->program,
861 ctx->vertex_shaders[vertex_shader],
862 shader->fragment_shader);
864 shader->mvp_location =
865 dispatch->GetUniformLocation (shader->program,
866 "ModelViewProjectionMatrix");
868 for (i = 0; i < 2; i++) {
869 shader->constant_location[i] =
870 _cairo_gl_get_op_uniform_location (ctx, shader, i, "constant");
871 shader->a_location[i] =
872 _cairo_gl_get_op_uniform_location (ctx, shader, i, "a");
873 shader->circle_d_location[i] =
874 _cairo_gl_get_op_uniform_location (ctx, shader, i, "circle_d");
875 shader->radius_0_location[i] =
876 _cairo_gl_get_op_uniform_location (ctx, shader, i, "radius_0");
877 shader->texdims_location[i] =
878 _cairo_gl_get_op_uniform_location (ctx, shader, i, "texdims");
879 shader->texgen_location[i] =
880 _cairo_gl_get_op_uniform_location (ctx, shader, i, "texgen");
883 return CAIRO_STATUS_SUCCESS;
885 FAILURE:
886 _cairo_gl_shader_fini (ctx, shader);
887 shader->fragment_shader = 0;
888 shader->program = 0;
890 return status;
893 /* We always bind the source to texture unit 0 if present, and mask to
894 * texture unit 1 if present, so we can just initialize these once at
895 * compile time.
897 static void
898 _cairo_gl_shader_set_samplers (cairo_gl_context_t *ctx,
899 cairo_gl_shader_t *shader)
901 cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
902 GLint location;
903 GLint saved_program;
905 /* We have to save/restore the current program because we might be
906 * asked for a different program while a shader is bound. This shouldn't
907 * be a performance issue, since this is only called once per compile.
909 glGetIntegerv (GL_CURRENT_PROGRAM, &saved_program);
910 dispatch->UseProgram (shader->program);
912 location = dispatch->GetUniformLocation (shader->program, "source_sampler");
913 if (location != -1) {
914 dispatch->Uniform1i (location, CAIRO_GL_TEX_SOURCE);
917 location = dispatch->GetUniformLocation (shader->program, "mask_sampler");
918 if (location != -1) {
919 dispatch->Uniform1i (location, CAIRO_GL_TEX_MASK);
922 dispatch->UseProgram (saved_program);
925 void
926 _cairo_gl_shader_bind_float (cairo_gl_context_t *ctx,
927 GLint location,
928 float value)
930 cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
931 assert (location != -1);
932 dispatch->Uniform1f (location, value);
935 void
936 _cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx,
937 GLint location,
938 float value0,
939 float value1)
941 cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
942 assert (location != -1);
943 dispatch->Uniform2f (location, value0, value1);
946 void
947 _cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx,
948 GLint location,
949 float value0,
950 float value1,
951 float value2)
953 cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
954 assert (location != -1);
955 dispatch->Uniform3f (location, value0, value1, value2);
958 void
959 _cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx,
960 GLint location,
961 float value0, float value1,
962 float value2, float value3)
964 cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
965 assert (location != -1);
966 dispatch->Uniform4f (location, value0, value1, value2, value3);
969 void
970 _cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx,
971 GLint location,
972 const cairo_matrix_t* m)
974 cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
975 float gl_m[9] = {
976 m->xx, m->yx, 0,
977 m->xy, m->yy, 0,
978 m->x0, m->y0, 1
980 assert (location != -1);
981 dispatch->UniformMatrix3fv (location, 1, GL_FALSE, gl_m);
984 void
985 _cairo_gl_shader_bind_matrix4f (cairo_gl_context_t *ctx,
986 GLint location, GLfloat* gl_m)
988 cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
989 assert (location != -1);
990 dispatch->UniformMatrix4fv (location, 1, GL_FALSE, gl_m);
993 void
994 _cairo_gl_set_shader (cairo_gl_context_t *ctx,
995 cairo_gl_shader_t *shader)
997 if (ctx->current_shader == shader)
998 return;
1000 if (shader)
1001 ctx->dispatch.UseProgram (shader->program);
1002 else
1003 ctx->dispatch.UseProgram (0);
1005 ctx->current_shader = shader;
1008 cairo_status_t
1009 _cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx,
1010 cairo_gl_operand_t *source,
1011 cairo_gl_operand_t *mask,
1012 cairo_bool_t use_coverage,
1013 cairo_gl_shader_in_t in,
1014 cairo_gl_shader_t **shader)
1016 cairo_shader_cache_entry_t lookup, *entry;
1017 char *fs_source;
1018 cairo_status_t status;
1020 lookup.ctx = ctx;
1022 lookup.vertex = cairo_gl_var_type_hash (cairo_gl_operand_get_var_type (source),
1023 cairo_gl_operand_get_var_type (mask),
1024 use_coverage,
1025 CAIRO_GL_VAR_NONE);
1027 lookup.src = source->type;
1028 lookup.mask = mask->type;
1029 lookup.dest = CAIRO_GL_OPERAND_NONE;
1030 lookup.use_coverage = use_coverage;
1031 lookup.in = in;
1032 lookup.src_gl_filter = _cairo_gl_operand_get_gl_filter (source);
1033 lookup.src_border_fade = _cairo_gl_shader_needs_border_fade (source);
1034 lookup.src_extend = _cairo_gl_operand_get_extend (source);
1035 lookup.mask_gl_filter = _cairo_gl_operand_get_gl_filter (mask);
1036 lookup.mask_border_fade = _cairo_gl_shader_needs_border_fade (mask);
1037 lookup.mask_extend = _cairo_gl_operand_get_extend (mask);
1038 lookup.base.hash = _cairo_gl_shader_cache_hash (&lookup);
1039 lookup.base.size = 1;
1041 entry = _cairo_cache_lookup (&ctx->shaders, &lookup.base);
1042 if (entry) {
1043 assert (entry->shader.program);
1044 *shader = &entry->shader;
1045 return CAIRO_STATUS_SUCCESS;
1048 status = cairo_gl_shader_get_fragment_source (ctx,
1050 source,
1051 mask,
1052 use_coverage,
1053 CAIRO_GL_OPERAND_NONE,
1054 &fs_source);
1055 if (unlikely (status))
1056 return status;
1058 entry = malloc (sizeof (cairo_shader_cache_entry_t));
1059 if (unlikely (entry == NULL)) {
1060 free (fs_source);
1061 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1064 memcpy (entry, &lookup, sizeof (cairo_shader_cache_entry_t));
1066 entry->ctx = ctx;
1067 _cairo_gl_shader_init (&entry->shader);
1068 status = _cairo_gl_shader_compile_and_link (ctx,
1069 &entry->shader,
1070 cairo_gl_operand_get_var_type (source),
1071 cairo_gl_operand_get_var_type (mask),
1072 use_coverage,
1073 fs_source);
1074 free (fs_source);
1076 if (unlikely (status)) {
1077 free (entry);
1078 return status;
1081 _cairo_gl_shader_set_samplers (ctx, &entry->shader);
1083 status = _cairo_cache_insert (&ctx->shaders, &entry->base);
1084 if (unlikely (status)) {
1085 _cairo_gl_shader_fini (ctx, &entry->shader);
1086 free (entry);
1087 return status;
1090 *shader = &entry->shader;
1092 return CAIRO_STATUS_SUCCESS;