1 /*
2 * Cogl
3 *
4 * A Low Level GPU Graphics and Utilities API
5 *
6 * Copyright (C) 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 #ifdef HAVE_CONFIG_H
35 #include "cogl-config.h"
36 #endif
37
38 #include "cogl-util.h"
39 #include "cogl-context-private.h"
40 #include "cogl-object-private.h"
41 #include "cogl-journal-private.h"
42 #include "cogl-attribute.h"
43 #include "cogl-attribute-private.h"
44 #include "cogl-pipeline.h"
45 #include "cogl-pipeline-private.h"
46 #include "cogl-pipeline-opengl-private.h"
47 #include "cogl-texture-private.h"
48 #include "cogl-framebuffer-private.h"
49 #include "cogl-indices-private.h"
50 #ifdef COGL_PIPELINE_PROGEND_GLSL
51 #include "cogl-pipeline-progend-glsl-private.h"
52 #endif
53 #include "cogl-private.h"
54 #include "cogl-gtype-private.h"
55
56 #include <string.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59
60 /* This isn't defined in the GLES headers */
61 #ifndef GL_UNSIGNED_INT
62 #define GL_UNSIGNED_INT 0x1405
63 #endif
64
65 static void _cogl_attribute_free (CoglAttribute *attribute);
66
67 COGL_OBJECT_DEFINE (Attribute, attribute);
68 COGL_GTYPE_DEFINE_CLASS (Attribute, attribute);
69
70 static CoglBool
validate_cogl_attribute_name(const char * name,char ** real_attribute_name,CoglAttributeNameID * name_id,CoglBool * normalized,int * layer_number)71 validate_cogl_attribute_name (const char *name,
72 char **real_attribute_name,
73 CoglAttributeNameID *name_id,
74 CoglBool *normalized,
75 int *layer_number)
76 {
77 name = name + 5; /* skip "cogl_" */
78
79 *normalized = FALSE;
80 *layer_number = 0;
81
82 if (strcmp (name, "position_in") == 0)
83 *name_id = COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY;
84 else if (strcmp (name, "color_in") == 0)
85 {
86 *name_id = COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY;
87 *normalized = TRUE;
88 }
89 else if (strcmp (name, "tex_coord_in") == 0)
90 {
91 *real_attribute_name = "cogl_tex_coord0_in";
92 *name_id = COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY;
93 }
94 else if (strncmp (name, "tex_coord", strlen ("tex_coord")) == 0)
95 {
96 char *endptr;
97 *layer_number = strtoul (name + 9, &endptr, 10);
98 if (strcmp (endptr, "_in") != 0)
99 {
100 g_warning ("Texture coordinate attributes should either be named "
101 "\"cogl_tex_coord_in\" or named with a texture unit index "
102 "like \"cogl_tex_coord2_in\"\n");
103 return FALSE;
104 }
105 *name_id = COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY;
106 }
107 else if (strcmp (name, "normal_in") == 0)
108 {
109 *name_id = COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY;
110 *normalized = TRUE;
111 }
112 else if (strcmp (name, "point_size_in") == 0)
113 *name_id = COGL_ATTRIBUTE_NAME_ID_POINT_SIZE_ARRAY;
114 else
115 {
116 g_warning ("Unknown cogl_* attribute name cogl_%s\n", name);
117 return FALSE;
118 }
119
120 return TRUE;
121 }
122
123 CoglAttributeNameState *
_cogl_attribute_register_attribute_name(CoglContext * context,const char * name)124 _cogl_attribute_register_attribute_name (CoglContext *context,
125 const char *name)
126 {
127 CoglAttributeNameState *name_state = g_new (CoglAttributeNameState, 1);
128 int name_index = context->n_attribute_names++;
129 char *name_copy = g_strdup (name);
130
131 name_state->name = NULL;
132 name_state->name_index = name_index;
133 if (strncmp (name, "cogl_", 5) == 0)
134 {
135 if (!validate_cogl_attribute_name (name,
136 &name_state->name,
137 &name_state->name_id,
138 &name_state->normalized_default,
139 &name_state->layer_number))
140 goto error;
141 }
142 else
143 {
144 name_state->name_id = COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY;
145 name_state->normalized_default = FALSE;
146 name_state->layer_number = 0;
147 }
148
149 if (name_state->name == NULL)
150 name_state->name = name_copy;
151
152 g_hash_table_insert (context->attribute_name_states_hash,
153 name_copy, name_state);
154
155 if (G_UNLIKELY (context->attribute_name_index_map == NULL))
156 context->attribute_name_index_map =
157 g_array_new (FALSE, FALSE, sizeof (void *));
158
159 g_array_set_size (context->attribute_name_index_map, name_index + 1);
160
161 g_array_index (context->attribute_name_index_map,
162 CoglAttributeNameState *, name_index) = name_state;
163
164 return name_state;
165
166 error:
167 free (name_state);
168 return NULL;
169 }
170
171 static CoglBool
validate_n_components(const CoglAttributeNameState * name_state,int n_components)172 validate_n_components (const CoglAttributeNameState *name_state,
173 int n_components)
174 {
175 switch (name_state->name_id)
176 {
177 case COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY:
178 if (G_UNLIKELY (n_components == 1))
179 {
180 g_critical ("glVertexPointer doesn't allow 1 component vertex "
181 "positions so we currently only support \"cogl_vertex\" "
182 "attributes where n_components == 2, 3 or 4");
183 return FALSE;
184 }
185 break;
186 case COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY:
187 if (G_UNLIKELY (n_components != 3 && n_components != 4))
188 {
189 g_critical ("glColorPointer expects 3 or 4 component colors so we "
190 "currently only support \"cogl_color\" attributes where "
191 "n_components == 3 or 4");
192 return FALSE;
193 }
194 break;
195 case COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY:
196 break;
197 case COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY:
198 if (G_UNLIKELY (n_components != 3))
199 {
200 g_critical ("glNormalPointer expects 3 component normals so we "
201 "currently only support \"cogl_normal\" attributes "
202 "where n_components == 3");
203 return FALSE;
204 }
205 break;
206 case COGL_ATTRIBUTE_NAME_ID_POINT_SIZE_ARRAY:
207 if (G_UNLIKELY (n_components != 1))
208 {
209 g_critical ("The point size attribute can only have one "
210 "component");
211 return FALSE;
212 }
213 break;
214 case COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY:
215 return TRUE;
216 }
217
218 return TRUE;
219 }
220
221 CoglAttribute *
cogl_attribute_new(CoglAttributeBuffer * attribute_buffer,const char * name,size_t stride,size_t offset,int n_components,CoglAttributeType type)222 cogl_attribute_new (CoglAttributeBuffer *attribute_buffer,
223 const char *name,
224 size_t stride,
225 size_t offset,
226 int n_components,
227 CoglAttributeType type)
228 {
229 CoglAttribute *attribute = g_slice_new (CoglAttribute);
230 CoglBuffer *buffer = COGL_BUFFER (attribute_buffer);
231 CoglContext *ctx = buffer->context;
232
233 attribute->is_buffered = TRUE;
234
235 attribute->name_state =
236 g_hash_table_lookup (ctx->attribute_name_states_hash, name);
237 if (!attribute->name_state)
238 {
239 CoglAttributeNameState *name_state =
240 _cogl_attribute_register_attribute_name (ctx, name);
241 if (!name_state)
242 goto error;
243 attribute->name_state = name_state;
244 }
245
246 attribute->d.buffered.attribute_buffer = cogl_object_ref (attribute_buffer);
247 attribute->d.buffered.stride = stride;
248 attribute->d.buffered.offset = offset;
249 attribute->d.buffered.n_components = n_components;
250 attribute->d.buffered.type = type;
251
252 attribute->immutable_ref = 0;
253
254 if (attribute->name_state->name_id != COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY)
255 {
256 if (!validate_n_components (attribute->name_state, n_components))
257 return NULL;
258 attribute->normalized =
259 attribute->name_state->normalized_default;
260 }
261 else
262 attribute->normalized = FALSE;
263
264 return _cogl_attribute_object_new (attribute);
265
266 error:
267 _cogl_attribute_free (attribute);
268 return NULL;
269 }
270
271 static CoglAttribute *
_cogl_attribute_new_const(CoglContext * context,const char * name,int n_components,int n_columns,CoglBool transpose,const float * value)272 _cogl_attribute_new_const (CoglContext *context,
273 const char *name,
274 int n_components,
275 int n_columns,
276 CoglBool transpose,
277 const float *value)
278 {
279 CoglAttribute *attribute = g_slice_new (CoglAttribute);
280
281 attribute->name_state =
282 g_hash_table_lookup (context->attribute_name_states_hash, name);
283 if (!attribute->name_state)
284 {
285 CoglAttributeNameState *name_state =
286 _cogl_attribute_register_attribute_name (context, name);
287 if (!name_state)
288 goto error;
289 attribute->name_state = name_state;
290 }
291
292 if (!validate_n_components (attribute->name_state, n_components))
293 goto error;
294
295 attribute->is_buffered = FALSE;
296 attribute->normalized = FALSE;
297
298 attribute->d.constant.context = cogl_object_ref (context);
299
300 attribute->d.constant.boxed.v.array = NULL;
301
302 if (n_columns == 1)
303 {
304 _cogl_boxed_value_set_float (&attribute->d.constant.boxed,
305 n_components,
306 1,
307 value);
308 }
309 else
310 {
311 /* FIXME: Up until GL[ES] 3 only square matrices were supported
312 * and we don't currently expose non-square matrices in Cogl.
313 */
314 _COGL_RETURN_VAL_IF_FAIL (n_columns == n_components, NULL);
315 _cogl_boxed_value_set_matrix (&attribute->d.constant.boxed,
316 n_columns,
317 1,
318 transpose,
319 value);
320 }
321
322 return _cogl_attribute_object_new (attribute);
323
324 error:
325 _cogl_attribute_free (attribute);
326 return NULL;
327 }
328
329 CoglAttribute *
cogl_attribute_new_const_1f(CoglContext * context,const char * name,float value)330 cogl_attribute_new_const_1f (CoglContext *context,
331 const char *name,
332 float value)
333 {
334 return _cogl_attribute_new_const (context,
335 name,
336 1, /* n_components */
337 1, /* 1 column vector */
338 FALSE, /* no transpose */
339 &value);
340 }
341
342 CoglAttribute *
cogl_attribute_new_const_2fv(CoglContext * context,const char * name,const float * value)343 cogl_attribute_new_const_2fv (CoglContext *context,
344 const char *name,
345 const float *value)
346 {
347 return _cogl_attribute_new_const (context,
348 name,
349 2, /* n_components */
350 1, /* 1 column vector */
351 FALSE, /* no transpose */
352 value);
353 }
354
355 CoglAttribute *
cogl_attribute_new_const_3fv(CoglContext * context,const char * name,const float * value)356 cogl_attribute_new_const_3fv (CoglContext *context,
357 const char *name,
358 const float *value)
359 {
360 return _cogl_attribute_new_const (context,
361 name,
362 3, /* n_components */
363 1, /* 1 column vector */
364 FALSE, /* no transpose */
365 value);
366 }
367
368 CoglAttribute *
cogl_attribute_new_const_4fv(CoglContext * context,const char * name,const float * value)369 cogl_attribute_new_const_4fv (CoglContext *context,
370 const char *name,
371 const float *value)
372 {
373 return _cogl_attribute_new_const (context,
374 name,
375 4, /* n_components */
376 1, /* 1 column vector */
377 FALSE, /* no transpose */
378 value);
379 }
380
381 CoglAttribute *
cogl_attribute_new_const_2f(CoglContext * context,const char * name,float component0,float component1)382 cogl_attribute_new_const_2f (CoglContext *context,
383 const char *name,
384 float component0,
385 float component1)
386 {
387 float vec2[2] = { component0, component1 };
388 return _cogl_attribute_new_const (context,
389 name,
390 2, /* n_components */
391 1, /* 1 column vector */
392 FALSE, /* no transpose */
393 vec2);
394 }
395
396 CoglAttribute *
cogl_attribute_new_const_3f(CoglContext * context,const char * name,float component0,float component1,float component2)397 cogl_attribute_new_const_3f (CoglContext *context,
398 const char *name,
399 float component0,
400 float component1,
401 float component2)
402 {
403 float vec3[3] = { component0, component1, component2 };
404 return _cogl_attribute_new_const (context,
405 name,
406 3, /* n_components */
407 1, /* 1 column vector */
408 FALSE, /* no transpose */
409 vec3);
410 }
411
412 CoglAttribute *
cogl_attribute_new_const_4f(CoglContext * context,const char * name,float component0,float component1,float component2,float component3)413 cogl_attribute_new_const_4f (CoglContext *context,
414 const char *name,
415 float component0,
416 float component1,
417 float component2,
418 float component3)
419 {
420 float vec4[4] = { component0, component1, component2, component3 };
421 return _cogl_attribute_new_const (context,
422 name,
423 4, /* n_components */
424 1, /* 1 column vector */
425 FALSE, /* no transpose */
426 vec4);
427 }
428
429 CoglAttribute *
cogl_attribute_new_const_2x2fv(CoglContext * context,const char * name,const float * matrix2x2,CoglBool transpose)430 cogl_attribute_new_const_2x2fv (CoglContext *context,
431 const char *name,
432 const float *matrix2x2,
433 CoglBool transpose)
434 {
435 return _cogl_attribute_new_const (context,
436 name,
437 2, /* n_components */
438 2, /* 2 column vector */
439 FALSE, /* no transpose */
440 matrix2x2);
441 }
442
443 CoglAttribute *
cogl_attribute_new_const_3x3fv(CoglContext * context,const char * name,const float * matrix3x3,CoglBool transpose)444 cogl_attribute_new_const_3x3fv (CoglContext *context,
445 const char *name,
446 const float *matrix3x3,
447 CoglBool transpose)
448 {
449 return _cogl_attribute_new_const (context,
450 name,
451 3, /* n_components */
452 3, /* 3 column vector */
453 FALSE, /* no transpose */
454 matrix3x3);
455 }
456
457 CoglAttribute *
cogl_attribute_new_const_4x4fv(CoglContext * context,const char * name,const float * matrix4x4,CoglBool transpose)458 cogl_attribute_new_const_4x4fv (CoglContext *context,
459 const char *name,
460 const float *matrix4x4,
461 CoglBool transpose)
462 {
463 return _cogl_attribute_new_const (context,
464 name,
465 4, /* n_components */
466 4, /* 4 column vector */
467 FALSE, /* no transpose */
468 matrix4x4);
469 }
470
471 CoglBool
cogl_attribute_get_normalized(CoglAttribute * attribute)472 cogl_attribute_get_normalized (CoglAttribute *attribute)
473 {
474 _COGL_RETURN_VAL_IF_FAIL (cogl_is_attribute (attribute), FALSE);
475
476 return attribute->normalized;
477 }
478
479 static void
warn_about_midscene_changes(void)480 warn_about_midscene_changes (void)
481 {
482 static CoglBool seen = FALSE;
483 if (!seen)
484 {
485 g_warning ("Mid-scene modification of attributes has "
486 "undefined results\n");
487 seen = TRUE;
488 }
489 }
490
491 void
cogl_attribute_set_normalized(CoglAttribute * attribute,CoglBool normalized)492 cogl_attribute_set_normalized (CoglAttribute *attribute,
493 CoglBool normalized)
494 {
495 _COGL_RETURN_IF_FAIL (cogl_is_attribute (attribute));
496
497 if (G_UNLIKELY (attribute->immutable_ref))
498 warn_about_midscene_changes ();
499
500 attribute->normalized = normalized;
501 }
502
503 CoglAttributeBuffer *
cogl_attribute_get_buffer(CoglAttribute * attribute)504 cogl_attribute_get_buffer (CoglAttribute *attribute)
505 {
506 _COGL_RETURN_VAL_IF_FAIL (cogl_is_attribute (attribute), NULL);
507 _COGL_RETURN_VAL_IF_FAIL (attribute->is_buffered, NULL);
508
509 return attribute->d.buffered.attribute_buffer;
510 }
511
512 void
cogl_attribute_set_buffer(CoglAttribute * attribute,CoglAttributeBuffer * attribute_buffer)513 cogl_attribute_set_buffer (CoglAttribute *attribute,
514 CoglAttributeBuffer *attribute_buffer)
515 {
516 _COGL_RETURN_IF_FAIL (cogl_is_attribute (attribute));
517 _COGL_RETURN_IF_FAIL (attribute->is_buffered);
518
519 if (G_UNLIKELY (attribute->immutable_ref))
520 warn_about_midscene_changes ();
521
522 cogl_object_ref (attribute_buffer);
523
524 cogl_object_unref (attribute->d.buffered.attribute_buffer);
525 attribute->d.buffered.attribute_buffer = attribute_buffer;
526 }
527
528 CoglAttribute *
_cogl_attribute_immutable_ref(CoglAttribute * attribute)529 _cogl_attribute_immutable_ref (CoglAttribute *attribute)
530 {
531 CoglBuffer *buffer = COGL_BUFFER (attribute->d.buffered.attribute_buffer);
532
533 _COGL_RETURN_VAL_IF_FAIL (cogl_is_attribute (attribute), NULL);
534
535 attribute->immutable_ref++;
536 _cogl_buffer_immutable_ref (buffer);
537 return attribute;
538 }
539
540 void
_cogl_attribute_immutable_unref(CoglAttribute * attribute)541 _cogl_attribute_immutable_unref (CoglAttribute *attribute)
542 {
543 CoglBuffer *buffer = COGL_BUFFER (attribute->d.buffered.attribute_buffer);
544
545 _COGL_RETURN_IF_FAIL (cogl_is_attribute (attribute));
546 _COGL_RETURN_IF_FAIL (attribute->immutable_ref > 0);
547
548 attribute->immutable_ref--;
549 _cogl_buffer_immutable_unref (buffer);
550 }
551
552 static void
_cogl_attribute_free(CoglAttribute * attribute)553 _cogl_attribute_free (CoglAttribute *attribute)
554 {
555 if (attribute->is_buffered)
556 cogl_object_unref (attribute->d.buffered.attribute_buffer);
557 else
558 _cogl_boxed_value_destroy (&attribute->d.constant.boxed);
559
560 g_slice_free (CoglAttribute, attribute);
561 }
562
563 static CoglBool
validate_layer_cb(CoglPipeline * pipeline,int layer_index,void * user_data)564 validate_layer_cb (CoglPipeline *pipeline,
565 int layer_index,
566 void *user_data)
567 {
568 CoglTexture *texture =
569 cogl_pipeline_get_layer_texture (pipeline, layer_index);
570 CoglFlushLayerState *state = user_data;
571 CoglBool status = TRUE;
572
573 /* invalid textures will be handled correctly in
574 * _cogl_pipeline_flush_layers_gl_state */
575 if (texture == NULL)
576 goto validated;
577
578 _cogl_texture_flush_journal_rendering (texture);
579
580 /* Give the texture a chance to know that we're rendering
581 non-quad shaped primitives. If the texture is in an atlas it
582 will be migrated */
583 _cogl_texture_ensure_non_quad_rendering (texture);
584
585 /* We need to ensure the mipmaps are ready before deciding
586 * anything else about the texture because the texture storate
587 * could completely change if it needs to be migrated out of the
588 * atlas and will affect how we validate the layer.
589 */
590 _cogl_pipeline_pre_paint_for_layer (pipeline, layer_index);
591
592 if (!_cogl_texture_can_hardware_repeat (texture))
593 {
594 g_warning ("Disabling layer %d of the current source material, "
595 "because texturing with the vertex buffer API is not "
596 "currently supported using sliced textures, or textures "
597 "with waste\n", layer_index);
598
599 /* XXX: maybe we can add a mechanism for users to forcibly use
600 * textures with waste where it would be their responsability to use
601 * texture coords in the range [0,1] such that sampling outside isn't
602 * required. We can then use a texture matrix (or a modification of
603 * the users own matrix) to map 1 to the edge of the texture data.
604 *
605 * Potentially, given the same guarantee as above we could also
606 * support a single sliced layer too. We would have to redraw the
607 * vertices once for each layer, each time with a fiddled texture
608 * matrix.
609 */
610 state->fallback_layers |= (1 << state->unit);
611 state->options.flags |= COGL_PIPELINE_FLUSH_FALLBACK_MASK;
612 }
613
614 validated:
615 state->unit++;
616 return status;
617 }
618
619 void
_cogl_flush_attributes_state(CoglFramebuffer * framebuffer,CoglPipeline * pipeline,CoglDrawFlags flags,CoglAttribute ** attributes,int n_attributes)620 _cogl_flush_attributes_state (CoglFramebuffer *framebuffer,
621 CoglPipeline *pipeline,
622 CoglDrawFlags flags,
623 CoglAttribute **attributes,
624 int n_attributes)
625 {
626 CoglContext *ctx = framebuffer->context;
627 CoglFlushLayerState layers_state;
628 CoglPipeline *copy = NULL;
629
630 if (!(flags & COGL_DRAW_SKIP_JOURNAL_FLUSH))
631 _cogl_journal_flush (framebuffer->journal);
632
633 layers_state.unit = 0;
634 layers_state.options.flags = 0;
635 layers_state.fallback_layers = 0;
636
637 if (!(flags & COGL_DRAW_SKIP_PIPELINE_VALIDATION))
638 cogl_pipeline_foreach_layer (pipeline,
639 validate_layer_cb,
640 &layers_state);
641
642 /* NB: _cogl_framebuffer_flush_state may disrupt various state (such
643 * as the pipeline state) when flushing the clip stack, so should
644 * always be done first when preparing to draw. We need to do this
645 * before setting up the array pointers because setting up the clip
646 * stack can cause some drawing which would change the array
647 * pointers. */
648 if (!(flags & COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH))
649 _cogl_framebuffer_flush_state (framebuffer,
650 framebuffer,
651 COGL_FRAMEBUFFER_STATE_ALL);
652
653 /* In cogl_read_pixels we have a fast-path when reading a single
654 * pixel and the scene is just comprised of simple rectangles still
655 * in the journal. For this optimization to work we need to track
656 * when the framebuffer really does get drawn to. */
657 _cogl_framebuffer_mark_mid_scene (framebuffer);
658 _cogl_framebuffer_mark_clear_clip_dirty (framebuffer);
659
660 if (G_UNLIKELY (!(flags & COGL_DRAW_SKIP_LEGACY_STATE)) &&
661 G_UNLIKELY (ctx->legacy_state_set) &&
662 _cogl_get_enable_legacy_state ())
663 {
664 copy = cogl_pipeline_copy (pipeline);
665 pipeline = copy;
666 _cogl_pipeline_apply_legacy_state (pipeline);
667 }
668
669 ctx->driver_vtable->flush_attributes_state (framebuffer,
670 pipeline,
671 &layers_state,
672 flags,
673 attributes,
674 n_attributes);
675
676 if (copy)
677 cogl_object_unref (copy);
678 }
679
680 int
_cogl_attribute_get_n_components(CoglAttribute * attribute)681 _cogl_attribute_get_n_components (CoglAttribute *attribute)
682 {
683 if (attribute->is_buffered)
684 return attribute->d.buffered.n_components;
685 else
686 return attribute->d.constant.boxed.size;
687 }
688