1 /*
2  * Cogl
3  *
4  * A Low Level GPU Graphics and Utilities API
5  *
6  * Copyright (C) 2008,2009,2010 Intel Corporation.
7  *
8  * Permission is hereby granted, free of charge, to any person
9  * obtaining a copy of this software and associated documentation
10  * files (the "Software"), to deal in the Software without
11  * restriction, including without limitation the rights to use, copy,
12  * modify, merge, publish, distribute, sublicense, and/or sell copies
13  * of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be
17  * included in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26  * SOFTWARE.
27  *
28  *
29  *
30  * Authors:
31  *   Robert Bragg <robert@linux.intel.com>
32  */
33 
34 #include "cogl-config.h"
35 
36 #include "cogl-debug.h"
37 #include "cogl-pipeline-private.h"
38 #include "cogl-context-private.h"
39 #include "cogl-texture-private.h"
40 #include "cogl-framebuffer-private.h"
41 #include "cogl-offscreen.h"
42 #include "driver/gl/cogl-util-gl-private.h"
43 #include "driver/gl/cogl-pipeline-opengl-private.h"
44 #include "driver/gl/cogl-texture-gl-private.h"
45 
46 #include "driver/gl/cogl-pipeline-progend-glsl-private.h"
47 
48 #include <test-fixtures/test-unit.h>
49 
50 #include <glib.h>
51 #include <string.h>
52 
53 /*
54  * GL/GLES compatibility defines for pipeline thingies:
55  */
56 
57 /* These aren't defined in the GLES headers */
58 #ifndef GL_POINT_SPRITE
59 #define GL_POINT_SPRITE 0x8861
60 #endif
61 #ifndef GL_COORD_REPLACE
62 #define GL_COORD_REPLACE 0x8862
63 #endif
64 #ifndef GL_CLAMP_TO_BORDER
65 #define GL_CLAMP_TO_BORDER 0x812d
66 #endif
67 
68 static void
texture_unit_init(CoglContext * ctx,CoglTextureUnit * unit,int index_)69 texture_unit_init (CoglContext *ctx,
70                    CoglTextureUnit *unit,
71                    int index_)
72 {
73   unit->index = index_;
74   unit->enabled_gl_target = 0;
75   unit->gl_texture = 0;
76   unit->gl_target = 0;
77   unit->dirty_gl_texture = FALSE;
78   unit->matrix_stack = cogl_matrix_stack_new (ctx);
79 
80   unit->layer = NULL;
81   unit->layer_changes_since_flush = 0;
82   unit->texture_storage_changed = FALSE;
83 }
84 
85 static void
texture_unit_free(CoglTextureUnit * unit)86 texture_unit_free (CoglTextureUnit *unit)
87 {
88   if (unit->layer)
89     cogl_object_unref (unit->layer);
90   cogl_object_unref (unit->matrix_stack);
91 }
92 
93 CoglTextureUnit *
_cogl_get_texture_unit(int index_)94 _cogl_get_texture_unit (int index_)
95 {
96   _COGL_GET_CONTEXT (ctx, NULL);
97   CoglGLContext *glctx = _cogl_driver_gl_context(ctx);
98 
99   if (glctx->texture_units->len < (index_ + 1))
100     {
101       int i;
102       int prev_len = glctx->texture_units->len;
103       glctx->texture_units = g_array_set_size (glctx->texture_units,
104                                                index_ + 1);
105       for (i = prev_len; i <= index_; i++)
106         {
107           CoglTextureUnit *unit =
108             &g_array_index (glctx->texture_units, CoglTextureUnit, i);
109 
110           texture_unit_init (ctx, unit, i);
111         }
112     }
113 
114   return &g_array_index (glctx->texture_units, CoglTextureUnit, index_);
115 }
116 
117 void
_cogl_destroy_texture_units(CoglContext * ctx)118 _cogl_destroy_texture_units (CoglContext *ctx)
119 {
120   int i;
121   CoglGLContext *glctx = _cogl_driver_gl_context(ctx);
122 
123   for (i = 0; i < glctx->texture_units->len; i++)
124     {
125       CoglTextureUnit *unit =
126         &g_array_index (glctx->texture_units, CoglTextureUnit, i);
127       texture_unit_free (unit);
128     }
129   g_array_free (glctx->texture_units, TRUE);
130 }
131 
132 void
_cogl_set_active_texture_unit(int unit_index)133 _cogl_set_active_texture_unit (int unit_index)
134 {
135   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
136   CoglGLContext *glctx = _cogl_driver_gl_context(ctx);
137 
138   if (glctx->active_texture_unit != unit_index)
139     {
140       GE (ctx, glActiveTexture (GL_TEXTURE0 + unit_index));
141       glctx->active_texture_unit = unit_index;
142     }
143 }
144 
145 /* Note: _cogl_bind_gl_texture_transient conceptually has slightly
146  * different semantics to OpenGL's glBindTexture because Cogl never
147  * cares about tracking multiple textures bound to different targets
148  * on the same texture unit.
149  *
150  * glBindTexture lets you bind multiple textures to a single texture
151  * unit if they are bound to different targets. So it does something
152  * like:
153  *   unit->current_texture[target] = texture;
154  *
155  * Cogl only lets you associate one texture with the currently active
156  * texture unit, so the target is basically a redundant parameter
157  * that's implicitly set on that texture.
158  *
159  * Technically this is just a thin wrapper around glBindTexture so
160  * actually it does have the GL semantics but it seems worth
161  * mentioning the conceptual difference in case anyone wonders why we
162  * don't associate the gl_texture with a gl_target in the
163  * CoglTextureUnit.
164  */
165 void
_cogl_bind_gl_texture_transient(GLenum gl_target,GLuint gl_texture)166 _cogl_bind_gl_texture_transient (GLenum gl_target,
167                                  GLuint gl_texture)
168 {
169   CoglTextureUnit *unit;
170 
171   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
172 
173   /* We choose to always make texture unit 1 active for transient
174    * binds so that in the common case where multitexturing isn't used
175    * we can simply ignore the state of this texture unit. Notably we
176    * didn't use a large texture unit (.e.g. (GL_MAX_TEXTURE_UNITS - 1)
177    * in case the driver doesn't have a sparse data structure for
178    * texture units.
179    */
180   _cogl_set_active_texture_unit (1);
181   unit = _cogl_get_texture_unit (1);
182 
183   if (unit->gl_texture == gl_texture && !unit->dirty_gl_texture)
184     return;
185 
186   GE (ctx, glBindTexture (gl_target, gl_texture));
187 
188   unit->dirty_gl_texture = TRUE;
189 }
190 
191 void
_cogl_delete_gl_texture(GLuint gl_texture)192 _cogl_delete_gl_texture (GLuint gl_texture)
193 {
194   int i;
195 
196   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
197   CoglGLContext *glctx = _cogl_driver_gl_context(ctx);
198 
199   for (i = 0; i < glctx->texture_units->len; i++)
200     {
201       CoglTextureUnit *unit =
202         &g_array_index (glctx->texture_units, CoglTextureUnit, i);
203 
204       if (unit->gl_texture == gl_texture)
205         {
206           unit->gl_texture = 0;
207           unit->gl_target = 0;
208           unit->dirty_gl_texture = FALSE;
209         }
210     }
211 
212   GE (ctx, glDeleteTextures (1, &gl_texture));
213 }
214 
215 /* Whenever the underlying GL texture storage of a CoglTexture is
216  * changed (e.g. due to migration out of a texture atlas) then we are
217  * notified. This lets us ensure that we reflush that texture's state
218  * if it is reused again with the same texture unit.
219  */
220 void
_cogl_pipeline_texture_storage_change_notify(CoglTexture * texture)221 _cogl_pipeline_texture_storage_change_notify (CoglTexture *texture)
222 {
223   int i;
224 
225   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
226   CoglGLContext *glctx = _cogl_driver_gl_context(ctx);
227 
228   for (i = 0; i < glctx->texture_units->len; i++)
229     {
230       CoglTextureUnit *unit =
231         &g_array_index (glctx->texture_units, CoglTextureUnit, i);
232 
233       if (unit->layer &&
234           _cogl_pipeline_layer_get_texture (unit->layer) == texture)
235         unit->texture_storage_changed = TRUE;
236 
237       /* NB: the texture may be bound to multiple texture units so
238        * we continue to check the rest */
239     }
240 }
241 
242 #if defined(HAVE_COGL_GLES2) || defined(HAVE_COGL_GL)
243 
244 static gboolean
blend_factor_uses_constant(GLenum blend_factor)245 blend_factor_uses_constant (GLenum blend_factor)
246 {
247   return (blend_factor == GL_CONSTANT_COLOR ||
248           blend_factor == GL_ONE_MINUS_CONSTANT_COLOR ||
249           blend_factor == GL_CONSTANT_ALPHA ||
250           blend_factor == GL_ONE_MINUS_CONSTANT_ALPHA);
251 }
252 
253 #endif
254 
255 static void
flush_depth_state(CoglContext * ctx,CoglDepthState * depth_state)256 flush_depth_state (CoglContext *ctx,
257                    CoglDepthState *depth_state)
258 {
259   gboolean depth_writing_enabled = depth_state->write_enabled;
260 
261   if (ctx->current_draw_buffer)
262     {
263       depth_writing_enabled &=
264         cogl_framebuffer_get_depth_write_enabled (ctx->current_draw_buffer);
265     }
266 
267   if (ctx->depth_test_enabled_cache != depth_state->test_enabled)
268     {
269       if (depth_state->test_enabled == TRUE)
270         {
271           GE (ctx, glEnable (GL_DEPTH_TEST));
272           if (ctx->current_draw_buffer)
273             cogl_framebuffer_set_depth_buffer_clear_needed (ctx->current_draw_buffer);
274         }
275       else
276         GE (ctx, glDisable (GL_DEPTH_TEST));
277       ctx->depth_test_enabled_cache = depth_state->test_enabled;
278     }
279 
280   if (ctx->depth_test_function_cache != depth_state->test_function &&
281       depth_state->test_enabled == TRUE)
282     {
283       GE (ctx, glDepthFunc (depth_state->test_function));
284       ctx->depth_test_function_cache = depth_state->test_function;
285     }
286 
287   if (ctx->depth_writing_enabled_cache != depth_writing_enabled)
288     {
289       GE (ctx, glDepthMask (depth_writing_enabled ?
290                             GL_TRUE : GL_FALSE));
291       ctx->depth_writing_enabled_cache = depth_writing_enabled;
292     }
293 
294   if ((ctx->depth_range_near_cache != depth_state->range_near ||
295        ctx->depth_range_far_cache != depth_state->range_far))
296     {
297       if (ctx->driver == COGL_DRIVER_GLES2)
298         GE (ctx, glDepthRangef (depth_state->range_near,
299                                 depth_state->range_far));
300       else
301         GE (ctx, glDepthRange (depth_state->range_near,
302                                depth_state->range_far));
303 
304       ctx->depth_range_near_cache = depth_state->range_near;
305       ctx->depth_range_far_cache = depth_state->range_far;
306     }
307 }
308 
309 UNIT_TEST (check_gl_blend_enable,
310            0 /* no requirements */,
311            0 /* no failure cases */)
312 {
313   CoglPipeline *pipeline = cogl_pipeline_new (test_ctx);
314 
315   /* By default blending should be disabled */
316   g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 0);
317 
318   cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 1, 1);
319   _cogl_framebuffer_flush_journal (test_fb);
320 
321   /* After drawing an opaque rectangle blending should still be
322    * disabled */
323   g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 0);
324 
325   cogl_pipeline_set_color4f (pipeline, 0, 0, 0, 0);
326   cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 1, 1);
327   _cogl_framebuffer_flush_journal (test_fb);
328 
329   /* After drawing a transparent rectangle blending should be enabled */
330   g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 1);
331 
332   cogl_pipeline_set_blend (pipeline, "RGBA=ADD(SRC_COLOR, 0)", NULL);
333   cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 1, 1);
334   _cogl_framebuffer_flush_journal (test_fb);
335 
336   /* After setting a blend string that effectively disables blending
337    * then blending should be disabled */
338   g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 0);
339 }
340 
341 static void
_cogl_pipeline_flush_color_blend_alpha_depth_state(CoglPipeline * pipeline,unsigned long pipelines_difference,gboolean with_color_attrib)342 _cogl_pipeline_flush_color_blend_alpha_depth_state (
343                                             CoglPipeline *pipeline,
344                                             unsigned long pipelines_difference,
345                                             gboolean      with_color_attrib)
346 {
347   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
348 
349   if (pipelines_difference & COGL_PIPELINE_STATE_BLEND)
350     {
351       CoglPipeline *authority =
352         _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_BLEND);
353       CoglPipelineBlendState *blend_state =
354         &authority->big_state->blend_state;
355 
356 #if defined(HAVE_COGL_GLES2) || defined(HAVE_COGL_GL)
357       if (blend_factor_uses_constant (blend_state->blend_src_factor_rgb) ||
358           blend_factor_uses_constant (blend_state
359                                       ->blend_src_factor_alpha) ||
360           blend_factor_uses_constant (blend_state->blend_dst_factor_rgb) ||
361           blend_factor_uses_constant (blend_state->blend_dst_factor_alpha))
362         {
363           float red =
364             cogl_color_get_red_float (&blend_state->blend_constant);
365           float green =
366             cogl_color_get_green_float (&blend_state->blend_constant);
367           float blue =
368             cogl_color_get_blue_float (&blend_state->blend_constant);
369           float alpha =
370             cogl_color_get_alpha_float (&blend_state->blend_constant);
371 
372 
373           GE (ctx, glBlendColor (red, green, blue, alpha));
374         }
375 
376       GE (ctx, glBlendEquationSeparate (blend_state->blend_equation_rgb,
377                                         blend_state->blend_equation_alpha));
378 
379       GE (ctx, glBlendFuncSeparate (blend_state->blend_src_factor_rgb,
380                                     blend_state->blend_dst_factor_rgb,
381                                     blend_state->blend_src_factor_alpha,
382                                     blend_state->blend_dst_factor_alpha));
383     }
384 #endif
385 
386   if (pipelines_difference & COGL_PIPELINE_STATE_DEPTH)
387     {
388       CoglPipeline *authority =
389         _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_DEPTH);
390       CoglDepthState *depth_state = &authority->big_state->depth_state;
391 
392       flush_depth_state (ctx, depth_state);
393     }
394 
395   if (pipelines_difference & COGL_PIPELINE_STATE_CULL_FACE)
396     {
397       CoglPipeline *authority =
398         _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_CULL_FACE);
399       CoglPipelineCullFaceState *cull_face_state
400         = &authority->big_state->cull_face_state;
401 
402       if (cull_face_state->mode == COGL_PIPELINE_CULL_FACE_MODE_NONE)
403         GE( ctx, glDisable (GL_CULL_FACE) );
404       else
405         {
406           gboolean invert_winding;
407 
408           GE( ctx, glEnable (GL_CULL_FACE) );
409 
410           switch (cull_face_state->mode)
411             {
412             case COGL_PIPELINE_CULL_FACE_MODE_NONE:
413               g_assert_not_reached ();
414 
415             case COGL_PIPELINE_CULL_FACE_MODE_FRONT:
416               GE( ctx, glCullFace (GL_FRONT) );
417               break;
418 
419             case COGL_PIPELINE_CULL_FACE_MODE_BACK:
420               GE( ctx, glCullFace (GL_BACK) );
421               break;
422 
423             case COGL_PIPELINE_CULL_FACE_MODE_BOTH:
424               GE( ctx, glCullFace (GL_FRONT_AND_BACK) );
425               break;
426             }
427 
428           invert_winding =
429             cogl_framebuffer_is_y_flipped (ctx->current_draw_buffer);
430 
431           switch (cull_face_state->front_winding)
432             {
433             case COGL_WINDING_CLOCKWISE:
434               GE( ctx, glFrontFace (invert_winding ? GL_CCW : GL_CW) );
435               break;
436 
437             case COGL_WINDING_COUNTER_CLOCKWISE:
438               GE( ctx, glFrontFace (invert_winding ? GL_CW : GL_CCW) );
439               break;
440             }
441         }
442     }
443 
444   if (pipeline->real_blend_enable != ctx->gl_blend_enable_cache)
445     {
446       if (pipeline->real_blend_enable)
447         GE (ctx, glEnable (GL_BLEND));
448       else
449         GE (ctx, glDisable (GL_BLEND));
450       /* XXX: we shouldn't update any other blend state if blending
451        * is disabled! */
452       ctx->gl_blend_enable_cache = pipeline->real_blend_enable;
453     }
454 }
455 
456 static int
get_max_activateable_texture_units(void)457 get_max_activateable_texture_units (void)
458 {
459   _COGL_GET_CONTEXT (ctx, 0);
460 
461   if (G_UNLIKELY (ctx->max_activateable_texture_units == -1))
462     {
463       GLint values[3];
464       int n_values = 0;
465       int i;
466 
467 #ifdef HAVE_COGL_GL
468       if (ctx->driver != COGL_DRIVER_GLES2)
469         {
470           /* GL_MAX_TEXTURE_COORDS defines the number of texture coordinates
471            * that can be uploaded (but doesn't necessarily relate to how many
472            * texture images can be sampled) */
473           GE (ctx, glGetIntegerv (GL_MAX_TEXTURE_COORDS, values + n_values++));
474 
475           GE (ctx, glGetIntegerv (GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
476                                   values + n_values++));
477         }
478 #endif /* HAVE_COGL_GL */
479 
480 #ifdef HAVE_COGL_GLES2
481       if (ctx->driver == COGL_DRIVER_GLES2)
482         {
483           GE (ctx, glGetIntegerv (GL_MAX_VERTEX_ATTRIBS, values + n_values));
484           /* Two of the vertex attribs need to be used for the position
485              and color */
486           values[n_values++] -= 2;
487 
488           GE (ctx, glGetIntegerv (GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
489                                   values + n_values++));
490         }
491 #endif
492 
493 #ifdef HAVE_COGL_GL
494       if (ctx->driver == COGL_DRIVER_GL)
495         {
496           /* GL_MAX_TEXTURE_UNITS defines the number of units that are
497              usable from the fixed function pipeline, therefore it isn't
498              available in GLES2. These are also tied to the number of
499              texture coordinates that can be uploaded so it should be less
500              than that available from the shader extensions */
501           GE (ctx, glGetIntegerv (GL_MAX_TEXTURE_UNITS,
502                                   values + n_values++));
503 
504         }
505 #endif
506 
507       g_assert (n_values <= G_N_ELEMENTS (values) &&
508                 n_values > 0);
509 
510       /* Use the maximum value */
511       ctx->max_activateable_texture_units = values[0];
512       for (i = 1; i < n_values; i++)
513         ctx->max_activateable_texture_units =
514           MAX (values[i], ctx->max_activateable_texture_units);
515     }
516 
517   return ctx->max_activateable_texture_units;
518 }
519 
520 typedef struct
521 {
522   int i;
523   unsigned long *layer_differences;
524 } CoglPipelineFlushLayerState;
525 
526 static gboolean
flush_layers_common_gl_state_cb(CoglPipelineLayer * layer,void * user_data)527 flush_layers_common_gl_state_cb (CoglPipelineLayer *layer, void *user_data)
528 {
529   CoglPipelineFlushLayerState *flush_state = user_data;
530   int                          unit_index = flush_state->i;
531   CoglTextureUnit             *unit = _cogl_get_texture_unit (unit_index);
532   unsigned long                layers_difference =
533     flush_state->layer_differences[unit_index];
534 
535   _COGL_GET_CONTEXT (ctx, FALSE);
536 
537   /* There may not be enough texture units so we can bail out if
538    * that's the case...
539    */
540   if (G_UNLIKELY (unit_index >= get_max_activateable_texture_units ()))
541     {
542       static gboolean shown_warning = FALSE;
543 
544       if (!shown_warning)
545         {
546           g_warning ("Your hardware does not have enough texture units"
547                      "to handle this many texture layers");
548           shown_warning = TRUE;
549         }
550       return FALSE;
551     }
552 
553   if (layers_difference & COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA)
554     {
555       CoglTexture *texture = _cogl_pipeline_layer_get_texture_real (layer);
556       GLuint gl_texture;
557       GLenum gl_target;
558 
559       if (texture == NULL)
560         texture = COGL_TEXTURE (ctx->default_gl_texture_2d_tex);
561 
562       cogl_texture_get_gl_texture (texture,
563                                    &gl_texture,
564                                    &gl_target);
565 
566       _cogl_set_active_texture_unit (unit_index);
567 
568       /* NB: There are several Cogl components and some code in
569        * Clutter that will temporarily bind arbitrary GL textures to
570        * query and modify texture object parameters. If you look at
571        * _cogl_bind_gl_texture_transient() you can see we make sure
572        * that such code always binds to texture unit 1 which means we
573        * can't rely on the unit->gl_texture state if unit->index == 1.
574        *
575        * Because texture unit 1 is a bit special we actually defer any
576        * necessary glBindTexture for it until the end of
577        * _cogl_pipeline_flush_gl_state().
578        *
579        * NB: we get notified whenever glDeleteTextures is used (see
580        * _cogl_delete_gl_texture()) where we invalidate
581        * unit->gl_texture references to deleted textures so it's safe
582        * to compare unit->gl_texture with gl_texture.  (Without the
583        * hook it would be possible to delete a GL texture and create a
584        * new one with the same name and comparing unit->gl_texture and
585        * gl_texture wouldn't detect that.)
586        *
587        * NB: for foreign textures we don't know how the deletion of
588        * the GL texture objects correspond to the deletion of the
589        * CoglTextures so if there was previously a foreign texture
590        * associated with the texture unit then we can't assume that we
591        * aren't seeing a recycled texture name so we have to bind.
592        */
593       if (unit->gl_texture != gl_texture)
594         {
595           if (unit_index == 1)
596             unit->dirty_gl_texture = TRUE;
597           else
598             GE (ctx, glBindTexture (gl_target, gl_texture));
599           unit->gl_texture = gl_texture;
600           unit->gl_target = gl_target;
601         }
602 
603       /* The texture_storage_changed boolean indicates if the
604        * CoglTexture's underlying GL texture storage has changed since
605        * it was flushed to the texture unit. We've just flushed the
606        * latest state so we can reset this. */
607       unit->texture_storage_changed = FALSE;
608     }
609 
610   if ((layers_difference & COGL_PIPELINE_LAYER_STATE_SAMPLER) &&
611       _cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS))
612     {
613       const CoglSamplerCacheEntry *sampler_state;
614 
615       sampler_state = _cogl_pipeline_layer_get_sampler_state (layer);
616 
617       GE( ctx, glBindSampler (unit_index, sampler_state->sampler_object) );
618     }
619 
620   cogl_object_ref (layer);
621   if (unit->layer != NULL)
622     cogl_object_unref (unit->layer);
623 
624   unit->layer = layer;
625   unit->layer_changes_since_flush = 0;
626 
627   flush_state->i++;
628 
629   return TRUE;
630 }
631 
632 static void
_cogl_pipeline_flush_common_gl_state(CoglPipeline * pipeline,unsigned long pipelines_difference,unsigned long * layer_differences,gboolean with_color_attrib)633 _cogl_pipeline_flush_common_gl_state (CoglPipeline  *pipeline,
634                                       unsigned long  pipelines_difference,
635                                       unsigned long *layer_differences,
636                                       gboolean       with_color_attrib)
637 {
638   CoglPipelineFlushLayerState state;
639 
640   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
641 
642   _cogl_pipeline_flush_color_blend_alpha_depth_state (pipeline,
643                                                       pipelines_difference,
644                                                       with_color_attrib);
645 
646   state.i = 0;
647   state.layer_differences = layer_differences;
648   _cogl_pipeline_foreach_layer_internal (pipeline,
649                                          flush_layers_common_gl_state_cb,
650                                          &state);
651 }
652 
653 /* Re-assert the layer's wrap modes on the given CoglTexture.
654  *
655  * Note: we don't simply forward the wrap modes to layer->texture
656  * since the actual texture being used may have been overridden.
657  */
658 static void
_cogl_pipeline_layer_forward_wrap_modes(CoglPipelineLayer * layer,CoglTexture * texture)659 _cogl_pipeline_layer_forward_wrap_modes (CoglPipelineLayer *layer,
660                                          CoglTexture *texture)
661 {
662   CoglSamplerCacheWrapMode wrap_mode_s, wrap_mode_t;
663   GLenum gl_wrap_mode_s, gl_wrap_mode_t;
664 
665   if (texture == NULL)
666     return;
667 
668   _cogl_pipeline_layer_get_wrap_modes (layer,
669                                        &wrap_mode_s,
670                                        &wrap_mode_t);
671 
672   /* Update the wrap mode on the texture object. The texture backend
673      should cache the value so that it will be a no-op if the object
674      already has the same wrap mode set. The backend is best placed to
675      do this because it knows how many of the coordinates will
676      actually be used (ie, a 1D texture only cares about the 's'
677      coordinate but a 3D texture would use all three). GL uses the
678      wrap mode as part of the texture object state but we are
679      pretending it's part of the per-layer environment state. This
680      will break if the application tries to use different modes in
681      different layers using the same texture. */
682 
683   if (wrap_mode_s == COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC)
684     gl_wrap_mode_s = GL_CLAMP_TO_EDGE;
685   else
686     gl_wrap_mode_s = wrap_mode_s;
687 
688   if (wrap_mode_t == COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC)
689     gl_wrap_mode_t = GL_CLAMP_TO_EDGE;
690   else
691     gl_wrap_mode_t = wrap_mode_t;
692 
693   _cogl_texture_gl_flush_legacy_texobj_wrap_modes (texture,
694                                                    gl_wrap_mode_s,
695                                                    gl_wrap_mode_t);
696 }
697 
698 void
_cogl_sampler_gl_init(CoglContext * context,CoglSamplerCacheEntry * entry)699 _cogl_sampler_gl_init (CoglContext *context, CoglSamplerCacheEntry *entry)
700 {
701   if (_cogl_has_private_feature (context,
702                                  COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS))
703     {
704       GE( context, glGenSamplers (1, &entry->sampler_object) );
705 
706       GE( context, glSamplerParameteri (entry->sampler_object,
707                                         GL_TEXTURE_MIN_FILTER,
708                                         entry->min_filter) );
709       GE( context, glSamplerParameteri (entry->sampler_object,
710                                         GL_TEXTURE_MAG_FILTER,
711                                         entry->mag_filter) );
712 
713       GE (context, glSamplerParameteri (entry->sampler_object,
714                                         GL_TEXTURE_WRAP_S,
715                                         entry->wrap_mode_s) );
716       GE (context, glSamplerParameteri (entry->sampler_object,
717                                         GL_TEXTURE_WRAP_T,
718                                         entry->wrap_mode_t) );
719     }
720   else
721     {
722       CoglGLContext *gl_context = context->driver_context;
723 
724       /* If sampler objects aren't supported then we'll invent a
725          unique number so that pipelines can still compare the
726          unique state just by comparing the sampler object
727          numbers */
728       entry->sampler_object = gl_context->next_fake_sampler_object_number++;
729     }
730 }
731 
732 void
_cogl_sampler_gl_free(CoglContext * context,CoglSamplerCacheEntry * entry)733 _cogl_sampler_gl_free (CoglContext *context, CoglSamplerCacheEntry *entry)
734 {
735   if (_cogl_has_private_feature (context,
736                                  COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS))
737     GE( context, glDeleteSamplers (1, &entry->sampler_object) );
738 }
739 
740 /* OpenGL associates the min/mag filters and repeat modes with the
741  * texture object not the texture unit so we always have to re-assert
742  * the filter and repeat modes whenever we use a texture since it may
743  * be referenced by multiple pipelines with different modes.
744  *
745  * This function is bypassed in favour of sampler objects if
746  * GL_ARB_sampler_objects is advertised. This fallback won't work if
747  * the same texture is bound to multiple layers with different sampler
748  * state.
749  */
750 static void
foreach_texture_unit_update_filter_and_wrap_modes(void)751 foreach_texture_unit_update_filter_and_wrap_modes (void)
752 {
753   int i;
754 
755   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
756   CoglGLContext *glctx = _cogl_driver_gl_context(ctx);
757 
758   for (i = 0; i < glctx->texture_units->len; i++)
759     {
760       CoglTextureUnit *unit =
761         &g_array_index (glctx->texture_units, CoglTextureUnit, i);
762 
763       if (unit->layer)
764         {
765           CoglTexture *texture = _cogl_pipeline_layer_get_texture (unit->layer);
766 
767           if (texture != NULL)
768             {
769               CoglPipelineFilter min;
770               CoglPipelineFilter mag;
771 
772               _cogl_pipeline_layer_get_filters (unit->layer, &min, &mag);
773               _cogl_texture_gl_flush_legacy_texobj_filters (texture, min, mag);
774 
775               _cogl_pipeline_layer_forward_wrap_modes (unit->layer, texture);
776             }
777         }
778     }
779 }
780 
781 typedef struct
782 {
783   int i;
784   unsigned long *layer_differences;
785 } CoglPipelineCompareLayersState;
786 
787 static gboolean
compare_layer_differences_cb(CoglPipelineLayer * layer,void * user_data)788 compare_layer_differences_cb (CoglPipelineLayer *layer, void *user_data)
789 {
790   CoglPipelineCompareLayersState *state = user_data;
791   CoglTextureUnit *unit = _cogl_get_texture_unit (state->i);
792 
793   if (unit->layer == layer)
794     state->layer_differences[state->i] = unit->layer_changes_since_flush;
795   else if (unit->layer)
796     {
797       state->layer_differences[state->i] = unit->layer_changes_since_flush;
798       state->layer_differences[state->i] |=
799         _cogl_pipeline_layer_compare_differences (layer, unit->layer);
800     }
801   else
802     state->layer_differences[state->i] = COGL_PIPELINE_LAYER_STATE_ALL_SPARSE;
803 
804   /* XXX: There is always a possibility that a CoglTexture's
805    * underlying GL texture storage has been changed since it was last
806    * bound to a texture unit which is why we have a callback into
807    * _cogl_pipeline_texture_storage_change_notify whenever a textures
808    * underlying GL texture storage changes which will set the
809    * unit->texture_intern_changed flag. If we see that's been set here
810    * then we force an update of the texture state...
811    */
812   if (unit->texture_storage_changed)
813     state->layer_differences[state->i] |=
814       COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA;
815 
816   state->i++;
817 
818   return TRUE;
819 }
820 
821 typedef struct
822 {
823   CoglFramebuffer *framebuffer;
824   const CoglPipelineVertend *vertend;
825   const CoglPipelineFragend *fragend;
826   CoglPipeline *pipeline;
827   unsigned long *layer_differences;
828   gboolean error_adding_layer;
829   gboolean added_layer;
830 } CoglPipelineAddLayerState;
831 
832 static gboolean
vertend_add_layer_cb(CoglPipelineLayer * layer,void * user_data)833 vertend_add_layer_cb (CoglPipelineLayer *layer,
834                       void *user_data)
835 {
836   CoglPipelineAddLayerState *state = user_data;
837   const CoglPipelineVertend *vertend = state->vertend;
838   CoglPipeline *pipeline = state->pipeline;
839   int unit_index = _cogl_pipeline_layer_get_unit_index (layer);
840 
841   /* Either generate per layer code snippets or setup the
842    * fixed function glTexEnv for each layer... */
843   if (G_LIKELY (vertend->add_layer (pipeline,
844                                     layer,
845                                     state->layer_differences[unit_index],
846                                     state->framebuffer)))
847     state->added_layer = TRUE;
848   else
849     {
850       state->error_adding_layer = TRUE;
851       return FALSE;
852     }
853 
854   return TRUE;
855 }
856 
857 static gboolean
fragend_add_layer_cb(CoglPipelineLayer * layer,void * user_data)858 fragend_add_layer_cb (CoglPipelineLayer *layer,
859                       void *user_data)
860 {
861   CoglPipelineAddLayerState *state = user_data;
862   const CoglPipelineFragend *fragend = state->fragend;
863   CoglPipeline *pipeline = state->pipeline;
864   int unit_index = _cogl_pipeline_layer_get_unit_index (layer);
865 
866   /* Either generate per layer code snippets or setup the
867    * fixed function glTexEnv for each layer... */
868   if (G_LIKELY (fragend->add_layer (pipeline,
869                                     layer,
870                                     state->layer_differences[unit_index])))
871     state->added_layer = TRUE;
872   else
873     {
874       state->error_adding_layer = TRUE;
875       return FALSE;
876     }
877 
878   return TRUE;
879 }
880 
881 /*
882  * _cogl_pipeline_flush_gl_state:
883  *
884  * Details of override options:
885  * ->fallback_mask: is a bitmask of the pipeline layers that need to be
886  *    replaced with the default, fallback textures. The fallback textures are
887  *    fully transparent textures so they hopefully won't contribute to the
888  *    texture combining.
889  *
890  *    The intention of fallbacks is to try and preserve
891  *    the number of layers the user is expecting so that texture coordinates
892  *    they gave will mostly still correspond to the textures they intended, and
893  *    have a fighting chance of looking close to their originally intended
894  *    result.
895  *
896  * ->disable_mask: is a bitmask of the pipeline layers that will simply have
897  *    texturing disabled. It's only really intended for disabling all layers
898  *    > X; i.e. we'd expect to see a contiguous run of 0 starting from the LSB
899  *    and at some point the remaining bits flip to 1. It might work to disable
900  *    arbitrary layers; though I'm not sure a.t.m how OpenGL would take to
901  *    that.
902  *
903  *    The intention of the disable_mask is for emitting geometry when the user
904  *    hasn't supplied enough texture coordinates for all the layers and it's
905  *    not possible to auto generate default texture coordinates for those
906  *    layers.
907  *
908  * ->layer0_override_texture: forcibly tells us to bind this GL texture name for
909  *    layer 0 instead of plucking the gl_texture from the CoglTexture of layer
910  *    0.
911  *
912  *    The intention of this is for any primitives that supports sliced textures.
913  *    The code will can iterate each of the slices and re-flush the pipeline
914  *    forcing the GL texture of each slice in turn.
915  *
916  * ->wrap_mode_overrides: overrides the wrap modes set on each
917  *    layer. This is used to implement the automatic wrap mode.
918  *
919  * XXX: It might also help if we could specify a texture matrix for code
920  *    dealing with slicing that would be multiplied with the users own matrix.
921  *
922  *    Normally texture coords in the range [0, 1] refer to the extents of the
923  *    texture, but when your GL texture represents a slice of the real texture
924  *    (from the users POV) then a texture matrix would be a neat way of
925  *    transforming the mapping for each slice.
926  *
927  *    Currently for textured rectangles we manually calculate the texture
928  *    coords for each slice based on the users given coords, but this solution
929  *    isn't ideal.
930  */
931 void
_cogl_pipeline_flush_gl_state(CoglContext * ctx,CoglPipeline * pipeline,CoglFramebuffer * framebuffer,gboolean with_color_attrib,gboolean unknown_color_alpha)932 _cogl_pipeline_flush_gl_state (CoglContext *ctx,
933                                CoglPipeline *pipeline,
934                                CoglFramebuffer *framebuffer,
935                                gboolean with_color_attrib,
936                                gboolean unknown_color_alpha)
937 {
938   CoglPipeline *current_pipeline = ctx->current_pipeline;
939   unsigned long pipelines_difference;
940   int n_layers;
941   unsigned long *layer_differences;
942   CoglTextureUnit *unit1;
943   const CoglPipelineProgend *progend;
944 
945   COGL_STATIC_TIMER (pipeline_flush_timer,
946                      "Mainloop", /* parent */
947                      "Material Flush",
948                      "The time spent flushing material state",
949                      0 /* no application private data */);
950 
951   COGL_TIMER_START (_cogl_uprof_context, pipeline_flush_timer);
952 
953   /* Bail out asap if we've been asked to re-flush the already current
954    * pipeline and we can see the pipeline hasn't changed */
955   if (current_pipeline == pipeline &&
956       ctx->current_pipeline_age == pipeline->age &&
957       ctx->current_pipeline_with_color_attrib == with_color_attrib &&
958       ctx->current_pipeline_unknown_color_alpha == unknown_color_alpha)
959     goto done;
960   else
961     {
962       /* Update derived state (currently just the 'real_blend_enable'
963        * state) and determine a mask of state that differs between the
964        * current pipeline and the one we are flushing.
965        *
966        * Note updating the derived state is done before doing any
967        * pipeline comparisons so that we can correctly compare the
968        * 'real_blend_enable' state itself.
969        */
970 
971       if (current_pipeline == pipeline)
972         {
973           pipelines_difference = ctx->current_pipeline_changes_since_flush;
974 
975           if (pipelines_difference & COGL_PIPELINE_STATE_AFFECTS_BLENDING ||
976               pipeline->unknown_color_alpha != unknown_color_alpha)
977             {
978               gboolean save_real_blend_enable = pipeline->real_blend_enable;
979 
980               _cogl_pipeline_update_real_blend_enable (pipeline,
981                                                        unknown_color_alpha);
982 
983               if (save_real_blend_enable != pipeline->real_blend_enable)
984                 pipelines_difference |= COGL_PIPELINE_STATE_REAL_BLEND_ENABLE;
985             }
986         }
987       else if (current_pipeline)
988         {
989           pipelines_difference = ctx->current_pipeline_changes_since_flush;
990 
991           _cogl_pipeline_update_real_blend_enable (pipeline,
992                                                    unknown_color_alpha);
993 
994           pipelines_difference |=
995             _cogl_pipeline_compare_differences (ctx->current_pipeline,
996                                                 pipeline);
997         }
998       else
999         {
1000           _cogl_pipeline_update_real_blend_enable (pipeline,
1001                                                    unknown_color_alpha);
1002 
1003           pipelines_difference = COGL_PIPELINE_STATE_ALL;
1004         }
1005     }
1006 
1007   /* Get a layer_differences mask for each layer to be flushed */
1008   n_layers = cogl_pipeline_get_n_layers (pipeline);
1009   if (n_layers)
1010     {
1011       CoglPipelineCompareLayersState state;
1012       layer_differences = g_alloca (sizeof (unsigned long) * n_layers);
1013       memset (layer_differences, 0, sizeof (unsigned long) * n_layers);
1014       state.i = 0;
1015       state.layer_differences = layer_differences;
1016       _cogl_pipeline_foreach_layer_internal (pipeline,
1017                                              compare_layer_differences_cb,
1018                                              &state);
1019     }
1020   else
1021     layer_differences = NULL;
1022 
1023   /* First flush everything that's the same regardless of which
1024    * pipeline backend is being used...
1025    *
1026    * 1) top level state:
1027    *  glColor (or skip if a vertex attribute is being used for color)
1028    *  blend state
1029    *  alpha test state (except for GLES 2.0)
1030    *
1031    * 2) then foreach layer:
1032    *  determine gl_target/gl_texture
1033    *  bind texture
1034    *
1035    *  Note: After _cogl_pipeline_flush_common_gl_state you can expect
1036    *  all state of the layers corresponding texture unit to be
1037    *  updated.
1038    */
1039   _cogl_pipeline_flush_common_gl_state (pipeline,
1040                                         pipelines_difference,
1041                                         layer_differences,
1042                                         with_color_attrib);
1043 
1044   /* Now flush the fragment, vertex and program state according to the
1045    * current progend backend.
1046    *
1047    * Note: Some backends may not support the current pipeline
1048    * configuration and in that case it will report and error and we
1049    * will look for a different backend.
1050    *
1051    * NB: if pipeline->progend != COGL_PIPELINE_PROGEND_UNDEFINED then
1052    * we have previously managed to successfully flush this pipeline
1053    * with the given progend so we will simply use that to avoid
1054    * fallback code paths.
1055    */
1056 
1057   do
1058     {
1059       const CoglPipelineVertend *vertend;
1060       const CoglPipelineFragend *fragend;
1061       CoglPipelineAddLayerState state;
1062 
1063       progend = _cogl_pipeline_progend;
1064 
1065       if (G_UNLIKELY (!progend->start (pipeline)))
1066         continue;
1067 
1068       vertend = _cogl_pipeline_vertend;
1069 
1070       vertend->start (pipeline,
1071                       n_layers,
1072                       pipelines_difference);
1073 
1074       state.framebuffer = framebuffer;
1075       state.vertend = vertend;
1076       state.pipeline = pipeline;
1077       state.layer_differences = layer_differences;
1078       state.error_adding_layer = FALSE;
1079       state.added_layer = FALSE;
1080 
1081       _cogl_pipeline_foreach_layer_internal (pipeline,
1082                                              vertend_add_layer_cb,
1083                                              &state);
1084 
1085       if (G_UNLIKELY (state.error_adding_layer))
1086         continue;
1087 
1088       if (G_UNLIKELY (!vertend->end (pipeline, pipelines_difference)))
1089         continue;
1090 
1091       /* Now prepare the fragment processing state (fragend)
1092        *
1093        * NB: We can't combine the setup of the vertend and fragend
1094        * since the backends that do code generation share
1095        * ctx->codegen_source_buffer as a scratch buffer.
1096        */
1097 
1098       fragend = _cogl_pipeline_fragend;
1099       state.fragend = fragend;
1100 
1101       fragend->start (pipeline,
1102                       n_layers,
1103                       pipelines_difference);
1104 
1105       _cogl_pipeline_foreach_layer_internal (pipeline,
1106                                              fragend_add_layer_cb,
1107                                              &state);
1108 
1109       if (G_UNLIKELY (state.error_adding_layer))
1110         continue;
1111 
1112       if (G_UNLIKELY (!fragend->end (pipeline, pipelines_difference)))
1113         continue;
1114 
1115       if (progend->end)
1116         progend->end (pipeline, pipelines_difference);
1117       break;
1118     }
1119   while (0);
1120 
1121   /* FIXME: This reference is actually resulting in lots of
1122    * copy-on-write reparenting because one-shot pipelines end up
1123    * living for longer than necessary and so any later modification of
1124    * the parent will cause a copy-on-write.
1125    *
1126    * XXX: The issue should largely go away when we switch to using
1127    * weak pipelines for overrides.
1128    */
1129   cogl_object_ref (pipeline);
1130   if (ctx->current_pipeline != NULL)
1131     cogl_object_unref (ctx->current_pipeline);
1132   ctx->current_pipeline = pipeline;
1133   ctx->current_pipeline_changes_since_flush = 0;
1134   ctx->current_pipeline_with_color_attrib = with_color_attrib;
1135   ctx->current_pipeline_unknown_color_alpha = unknown_color_alpha;
1136   ctx->current_pipeline_age = pipeline->age;
1137 
1138 done:
1139 
1140   progend = _cogl_pipeline_progend;
1141 
1142   /* We can't assume the color will be retained between flushes when
1143    * using the glsl progend because the generic attribute values are
1144    * not stored as part of the program object so they could be
1145    * overridden by any attribute changes in another program */
1146   if (!with_color_attrib)
1147     {
1148       int attribute;
1149       CoglPipeline *authority =
1150         _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_COLOR);
1151       int name_index = COGL_ATTRIBUTE_COLOR_NAME_INDEX;
1152 
1153       attribute =
1154         _cogl_pipeline_progend_glsl_get_attrib_location (pipeline, name_index);
1155       if (attribute != -1)
1156         GE (ctx,
1157             glVertexAttrib4f (attribute,
1158                               cogl_color_get_red_float (&authority->color),
1159                               cogl_color_get_green_float (&authority->color),
1160                               cogl_color_get_blue_float (&authority->color),
1161                               cogl_color_get_alpha_float (&authority->color)));
1162     }
1163 
1164   /* Give the progend a chance to update any uniforms that might not
1165    * depend on the material state. This is used on GLES2 to update the
1166    * matrices */
1167   if (progend->pre_paint)
1168     progend->pre_paint (pipeline, framebuffer);
1169 
1170   /* Handle the fact that OpenGL associates texture filter and wrap
1171    * modes with the texture objects not the texture units... */
1172   if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS))
1173     foreach_texture_unit_update_filter_and_wrap_modes ();
1174 
1175   /* If this pipeline has more than one layer then we always need
1176    * to make sure we rebind the texture for unit 1.
1177    *
1178    * NB: various components of Cogl may temporarily bind arbitrary
1179    * textures to texture unit 1 so they can query and modify texture
1180    * object parameters. cogl-pipeline.c (See
1181    * _cogl_bind_gl_texture_transient)
1182    */
1183   unit1 = _cogl_get_texture_unit (1);
1184   if (cogl_pipeline_get_n_layers (pipeline) > 1 && unit1->dirty_gl_texture)
1185     {
1186       _cogl_set_active_texture_unit (1);
1187       GE (ctx, glBindTexture (unit1->gl_target, unit1->gl_texture));
1188       unit1->dirty_gl_texture = FALSE;
1189     }
1190 
1191   COGL_TIMER_STOP (_cogl_uprof_context, pipeline_flush_timer);
1192 }
1193 
1194 void
_cogl_gl_set_uniform(CoglContext * ctx,GLint location,const CoglBoxedValue * value)1195 _cogl_gl_set_uniform (CoglContext *ctx,
1196                       GLint location,
1197                       const CoglBoxedValue *value)
1198 {
1199   switch (value->type)
1200     {
1201     case COGL_BOXED_NONE:
1202       break;
1203 
1204     case COGL_BOXED_INT:
1205       {
1206         const int *ptr;
1207 
1208         if (value->count == 1)
1209           ptr = value->v.int_value;
1210         else
1211           ptr = value->v.int_array;
1212 
1213         switch (value->size)
1214           {
1215           case 1:
1216             GE( ctx, glUniform1iv (location, value->count, ptr) );
1217             break;
1218           case 2:
1219             GE( ctx, glUniform2iv (location, value->count, ptr) );
1220             break;
1221           case 3:
1222             GE( ctx, glUniform3iv (location, value->count, ptr) );
1223             break;
1224           case 4:
1225             GE( ctx, glUniform4iv (location, value->count, ptr) );
1226             break;
1227           }
1228       }
1229       break;
1230 
1231     case COGL_BOXED_FLOAT:
1232       {
1233         const float *ptr;
1234 
1235         if (value->count == 1)
1236           ptr = value->v.float_value;
1237         else
1238           ptr = value->v.float_array;
1239 
1240         switch (value->size)
1241           {
1242           case 1:
1243             GE( ctx, glUniform1fv (location, value->count, ptr) );
1244             break;
1245           case 2:
1246             GE( ctx, glUniform2fv (location, value->count, ptr) );
1247             break;
1248           case 3:
1249             GE( ctx, glUniform3fv (location, value->count, ptr) );
1250             break;
1251           case 4:
1252             GE( ctx, glUniform4fv (location, value->count, ptr) );
1253             break;
1254           }
1255       }
1256       break;
1257 
1258     case COGL_BOXED_MATRIX:
1259       {
1260         const float *ptr;
1261 
1262         if (value->count == 1)
1263           ptr = value->v.matrix;
1264         else
1265           ptr = value->v.float_array;
1266 
1267         switch (value->size)
1268           {
1269           case 2:
1270             GE( ctx, glUniformMatrix2fv (location, value->count,
1271                                          FALSE, ptr) );
1272             break;
1273           case 3:
1274             GE( ctx, glUniformMatrix3fv (location, value->count,
1275                                          FALSE, ptr) );
1276             break;
1277           case 4:
1278             GE( ctx, glUniformMatrix4fv (location, value->count,
1279                                          FALSE, ptr) );
1280             break;
1281           }
1282       }
1283       break;
1284     }
1285 }
1286