void _cogl_buffer_unmap_for_fill_or_fallback (CoglBuffer *buffer) { CoglContext *ctx = buffer->context; _COGL_RETURN_IF_FAIL (ctx->buffer_map_fallback_in_use); ctx->buffer_map_fallback_in_use = FALSE; if ((buffer->flags & COGL_BUFFER_FLAG_MAPPED_FALLBACK)) { /* Note: don't try to catch OOM errors here since the use cases * we currently have for this api (the journal and path stroke * tesselator) don't have anything particularly sensible they * can do in response to a failure anyway so it seems better to * simply abort instead. * * If we find this is a problem for real world applications * then in the path tesselation case we could potentially add an * explicit cogl_path_tesselate_stroke() api that can throw an * error for the app to cache. For the journal we could * potentially flush the journal in smaller batches so we use * smaller buffers, though that would probably not help for * deferred renderers. */ cogl_buffer_set_data (buffer, ctx->buffer_map_fallback_offset, ctx->buffer_map_fallback_array->data, ctx->buffer_map_fallback_array->len, NULL); buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED_FALLBACK; } else cogl_buffer_unmap (buffer); }
CoglAttributeBuffer * cogl_attribute_buffer_new (CoglContext *context, size_t bytes, const void *data) { CoglAttributeBuffer *array = g_slice_new (CoglAttributeBuffer); CoglBool use_malloc; if (!(context->private_feature_flags & COGL_PRIVATE_FEATURE_VBOS)) use_malloc = TRUE; else use_malloc = FALSE; /* parent's constructor */ _cogl_buffer_initialize (COGL_BUFFER (array), context, bytes, use_malloc, COGL_BUFFER_BIND_TARGET_ATTRIBUTE_BUFFER, COGL_BUFFER_USAGE_HINT_ATTRIBUTE_BUFFER, COGL_BUFFER_UPDATE_HINT_STATIC); _cogl_attribute_buffer_object_new (array); if (data) cogl_buffer_set_data (COGL_BUFFER (array), 0, data, bytes); return array; }
CoglVertexArray * cogl_vertex_array_new (gsize bytes, const void *data) { CoglVertexArray *array = g_slice_new (CoglVertexArray); gboolean use_malloc; if (!cogl_features_available (COGL_FEATURE_VBOS)) use_malloc = TRUE; else use_malloc = FALSE; /* parent's constructor */ _cogl_buffer_initialize (COGL_BUFFER (array), bytes, use_malloc, COGL_BUFFER_BIND_TARGET_VERTEX_ARRAY, COGL_BUFFER_USAGE_HINT_VERTEX_ARRAY, COGL_BUFFER_UPDATE_HINT_STATIC); _cogl_vertex_array_object_new (array); if (data) cogl_buffer_set_data (COGL_BUFFER (array), 0, data, bytes); return array; }
CoglIndices * cogl_indices_new (CoglContext *context, CoglIndicesType type, const void *indices_data, int n_indices) { size_t buffer_bytes = sizeof_indices_type (type) * n_indices; CoglIndexBuffer *index_buffer = cogl_index_buffer_new (context, buffer_bytes); CoglBuffer *buffer = COGL_BUFFER (index_buffer); CoglIndices *indices; CoglError *ignore_error = NULL; cogl_buffer_set_data (buffer, 0, indices_data, buffer_bytes, &ignore_error); if (ignore_error) { cogl_error_free (ignore_error); cogl_object_unref (index_buffer); return NULL; } indices = cogl_indices_new_for_buffer (type, index_buffer, 0); cogl_object_unref (index_buffer); return indices; }
void _cogl_buffer_unmap_for_fill_or_fallback (CoglBuffer *buffer) { CoglContext *ctx = buffer->context; _COGL_RETURN_IF_FAIL (ctx->buffer_map_fallback_in_use); ctx->buffer_map_fallback_in_use = FALSE; if ((buffer->flags & COGL_BUFFER_FLAG_MAPPED_FALLBACK)) { cogl_buffer_set_data (buffer, 0, ctx->buffer_map_fallback_array->data, buffer->size); buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED_FALLBACK; } else cogl_buffer_unmap (buffer); }
CoglIndices * cogl_indices_new (CoglIndicesType type, const void *indices_data, int n_indices) { size_t array_bytes = sizeof_indices_type (type) * n_indices; CoglIndexArray *array = cogl_index_array_new (array_bytes); CoglBuffer *buffer = COGL_BUFFER (array); CoglIndices *indices; cogl_buffer_set_data (buffer, 0, indices_data, array_bytes); indices = cogl_indices_new_for_array (type, array, 0); cogl_object_unref (array); return indices; }
CoglIndices * cogl_indices_new (CoglIndicesType type, const void *indices_data, int n_indices) { size_t buffer_bytes = sizeof_indices_type (type) * n_indices; CoglIndexBuffer *index_buffer = cogl_index_buffer_new (buffer_bytes); CoglBuffer *buffer = COGL_BUFFER (index_buffer); CoglIndices *indices; cogl_buffer_set_data (buffer, 0, indices_data, buffer_bytes); indices = cogl_indices_new_for_buffer (type, index_buffer, 0); cogl_object_unref (index_buffer); return indices; }
static void emit_vertex_buffer_geometry (CoglFramebuffer *fb, CoglPipeline *pipeline, CoglPangoDisplayListNode *node) { CoglContext *ctx = fb->context; /* It's expensive to go through the Cogl journal for large runs * of text in part because the journal transforms the quads in software * to avoid changing the modelview matrix. So for larger runs of text * we load the vertices into a VBO, and this has the added advantage * that if the text doesn't change from frame to frame the VBO can * be re-used avoiding the repeated cost of validating the data and * mapping it into the GPU... */ if (node->d.texture.primitive == NULL) { CoglAttributeBuffer *buffer; CoglVertexP2T2 *verts, *v; int n_verts; CoglBool allocated = FALSE; CoglAttribute *attributes[2]; CoglPrimitive *prim; int i; CoglError *ignore_error = NULL; n_verts = node->d.texture.rectangles->len * 4; buffer = cogl_attribute_buffer_new_with_size (ctx, n_verts * sizeof (CoglVertexP2T2)); if ((verts = cogl_buffer_map (COGL_BUFFER (buffer), COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD, &ignore_error)) == NULL) { cogl_error_free (ignore_error); verts = g_new (CoglVertexP2T2, n_verts); allocated = TRUE; } v = verts; /* Copy the rectangles into the buffer and expand into four vertices instead of just two */ for (i = 0; i < node->d.texture.rectangles->len; i++) { const CoglPangoDisplayListRectangle *rectangle = &g_array_index (node->d.texture.rectangles, CoglPangoDisplayListRectangle, i); v->x = rectangle->x_1; v->y = rectangle->y_1; v->s = rectangle->s_1; v->t = rectangle->t_1; v++; v->x = rectangle->x_1; v->y = rectangle->y_2; v->s = rectangle->s_1; v->t = rectangle->t_2; v++; v->x = rectangle->x_2; v->y = rectangle->y_2; v->s = rectangle->s_2; v->t = rectangle->t_2; v++; v->x = rectangle->x_2; v->y = rectangle->y_1; v->s = rectangle->s_2; v->t = rectangle->t_1; v++; } if (allocated) { cogl_buffer_set_data (COGL_BUFFER (buffer), 0, /* offset */ verts, sizeof (CoglVertexP2T2) * n_verts, NULL); g_free (verts); } else cogl_buffer_unmap (COGL_BUFFER (buffer)); attributes[0] = cogl_attribute_new (buffer, "cogl_position_in", sizeof (CoglVertexP2T2), G_STRUCT_OFFSET (CoglVertexP2T2, x), 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT); attributes[1] = cogl_attribute_new (buffer, "cogl_tex_coord0_in", sizeof (CoglVertexP2T2), G_STRUCT_OFFSET (CoglVertexP2T2, s), 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT); prim = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, n_verts, attributes, 2 /* n_attributes */); #ifdef CLUTTER_COGL_HAS_GL if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_QUADS)) cogl_primitive_set_mode (prim, GL_QUADS); else #endif { /* GLES doesn't support GL_QUADS so instead we use a VBO with indexed vertices to generate GL_TRIANGLES from the quads */ CoglIndices *indices = cogl_get_rectangle_indices (ctx, node->d.texture.rectangles->len); cogl_primitive_set_indices (prim, indices, node->d.texture.rectangles->len * 6); } node->d.texture.primitive = prim; cogl_object_unref (buffer); cogl_object_unref (attributes[0]); cogl_object_unref (attributes[1]); } cogl_primitive_draw (node->d.texture.primitive, fb, pipeline); }
static void clutter_deform_effect_paint_target (ClutterOffscreenEffect *effect) { ClutterDeformEffect *self= CLUTTER_DEFORM_EFFECT (effect); ClutterDeformEffectPrivate *priv = self->priv; CoglHandle material; CoglPipeline *pipeline; CoglDepthState depth_state; CoglFramebuffer *fb = cogl_get_draw_framebuffer (); if (priv->is_dirty) { ClutterRect rect; gboolean mapped_buffer; CoglVertexP3T2C4 *verts; ClutterActor *actor; gfloat width, height; guint opacity; gint i, j; actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); opacity = clutter_actor_get_paint_opacity (actor); /* if we don't have a target size, fall back to the actor's * allocation, though wrong it might be */ if (clutter_offscreen_effect_get_target_rect (effect, &rect)) { width = clutter_rect_get_width (&rect); height = clutter_rect_get_height (&rect); } else clutter_actor_get_size (actor, &width, &height); /* XXX ideally, the sub-classes should tell us what they * changed in the texture vertices; we then would be able to * avoid resubmitting the same data, if it did not change. for * the time being, we resubmit everything */ verts = cogl_buffer_map (COGL_BUFFER (priv->buffer), COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD); /* If the map failed then we'll resort to allocating a temporary buffer */ if (verts == NULL) { mapped_buffer = FALSE; verts = g_malloc (sizeof (*verts) * priv->n_vertices); } else mapped_buffer = TRUE; for (i = 0; i < priv->y_tiles + 1; i++) { for (j = 0; j < priv->x_tiles + 1; j++) { CoglVertexP3T2C4 *vertex_out; CoglTextureVertex vertex; /* CoglTextureVertex isn't an ideal structure to use for this because it contains a CoglColor. The internal layout of CoglColor is mean to be private so Clutter can not pass a pointer to it as a vertex attribute. Also it contains padding so we end up storing more data in the vertex buffer than we need to. Instead we let the application modify a dummy vertex and then copy the details back out to a more well-defined struct */ vertex.tx = (float) j / priv->x_tiles; vertex.ty = (float) i / priv->y_tiles; vertex.x = width * vertex.tx; vertex.y = height * vertex.ty; vertex.z = 0.0f; cogl_color_init_from_4ub (&vertex.color, 255, 255, 255, opacity); clutter_deform_effect_deform_vertex (self, width, height, &vertex); vertex_out = verts + i * (priv->x_tiles + 1) + j; vertex_out->x = vertex.x; vertex_out->y = vertex.y; vertex_out->z = vertex.z; vertex_out->s = vertex.tx; vertex_out->t = vertex.ty; vertex_out->r = cogl_color_get_red_byte (&vertex.color); vertex_out->g = cogl_color_get_green_byte (&vertex.color); vertex_out->b = cogl_color_get_blue_byte (&vertex.color); vertex_out->a = cogl_color_get_alpha_byte (&vertex.color); } } if (mapped_buffer) cogl_buffer_unmap (COGL_BUFFER (priv->buffer)); else { cogl_buffer_set_data (COGL_BUFFER (priv->buffer), 0, /* offset */ verts, sizeof (*verts) * priv->n_vertices); g_free (verts); } priv->is_dirty = FALSE; } material = clutter_offscreen_effect_get_target (effect); pipeline = COGL_PIPELINE (material); /* enable depth testing */ cogl_depth_state_init (&depth_state); cogl_depth_state_set_test_enabled (&depth_state, TRUE); cogl_pipeline_set_depth_state (pipeline, &depth_state, NULL); /* enable backface culling if we have a back material */ if (priv->back_pipeline != NULL) cogl_pipeline_set_cull_face_mode (pipeline, COGL_PIPELINE_CULL_FACE_MODE_BACK); /* draw the front */ if (material != NULL) cogl_framebuffer_draw_primitive (fb, pipeline, priv->primitive); /* draw the back */ if (priv->back_pipeline != NULL) { CoglPipeline *back_pipeline; /* We probably shouldn't be modifying the user's material so instead we make a temporary copy */ back_pipeline = cogl_pipeline_copy (priv->back_pipeline); cogl_pipeline_set_depth_state (back_pipeline, &depth_state, NULL); cogl_pipeline_set_cull_face_mode (back_pipeline, COGL_PIPELINE_CULL_FACE_MODE_FRONT); cogl_framebuffer_draw_primitive (fb, back_pipeline, priv->primitive); cogl_object_unref (back_pipeline); } if (G_UNLIKELY (priv->lines_primitive != NULL)) { CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); CoglPipeline *lines_pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_color4f (lines_pipeline, 1.0, 0, 0, 1.0); cogl_framebuffer_draw_primitive (fb, lines_pipeline, priv->lines_primitive); cogl_object_unref (lines_pipeline); } }
static void paint (Data *data) { int i; float diff_time; /* Update all of the firework's positions */ for (i = 0; i < N_FIREWORKS; i++) { Firework *firework = data->fireworks + i; if ((fabsf (firework->x - firework->start_x) > 2.0f) || firework->y < -1.0f) { firework->size = g_random_double_range (0.001f, 0.1f); firework->start_x = 1.0f + firework->size; firework->start_y = -1.0f; firework->initial_x_velocity = g_random_double_range (-0.1f, -2.0f); firework->initial_y_velocity = g_random_double_range (0.1f, 4.0f); g_timer_reset (firework->timer); /* Pick a random color out of six */ if (g_random_boolean ()) { memset (&firework->color, 0, sizeof (Color)); ((uint8_t *) &firework->color)[g_random_int_range (0, 3)] = 255; } else { memset (&firework->color, 255, sizeof (Color)); ((uint8_t *) &firework->color)[g_random_int_range (0, 3)] = 0; } firework->color.alpha = 255; /* Fire some of the fireworks from the other side */ if (g_random_boolean ()) { firework->start_x = -firework->start_x; firework->initial_x_velocity = -firework->initial_x_velocity; } } diff_time = g_timer_elapsed (firework->timer, NULL); firework->x = (firework->start_x + firework->initial_x_velocity * diff_time); firework->y = ((firework->initial_y_velocity * diff_time + 0.5f * GRAVITY * diff_time * diff_time) + firework->start_y); } diff_time = g_timer_elapsed (data->last_spark_time, NULL); if (diff_time < 0.0f || diff_time >= TIME_PER_SPARK) { /* Add a new spark for each firework, overwriting the oldest ones */ for (i = 0; i < N_FIREWORKS; i++) { Spark *spark = data->sparks + data->next_spark_num; Firework *firework = data->fireworks + i; spark->x = (firework->x + g_random_double_range (-firework->size / 2.0f, firework->size / 2.0f)); spark->y = (firework->y + g_random_double_range (-firework->size / 2.0f, firework->size / 2.0f)); spark->base_color = firework->color; data->next_spark_num = (data->next_spark_num + 1) & (N_SPARKS - 1); } /* Update the colour of each spark */ for (i = 0; i < N_SPARKS; i++) { float color_value; /* First spark is the oldest */ Spark *spark = data->sparks + ((data->next_spark_num + i) & (N_SPARKS - 1)); color_value = i / (N_SPARKS - 1.0f); spark->color.red = spark->base_color.red * color_value; spark->color.green = spark->base_color.green * color_value; spark->color.blue = spark->base_color.blue * color_value; spark->color.alpha = 255.0f * color_value; } g_timer_reset (data->last_spark_time); } cogl_buffer_set_data (data->attribute_buffer, 0, /* offset */ data->sparks, sizeof (data->sparks), NULL /* error */); cogl_framebuffer_clear4f (data->fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); cogl_primitive_draw (data->primitive, data->fb, data->pipeline); cogl_onscreen_swap_buffers (data->fb); }
void cogl_polygon (const CoglTextureVertex *vertices, unsigned int n_vertices, CoglBool use_color) { CoglPipeline *pipeline; ValidateState validate_state; int n_layers; int n_attributes; CoglAttribute **attributes; int i; unsigned int stride; size_t stride_bytes; CoglAttributeBuffer *attribute_buffer; float *v; _COGL_GET_CONTEXT (ctx, NO_RETVAL); pipeline = cogl_get_source (); validate_state.original_pipeline = pipeline; validate_state.pipeline = pipeline; cogl_pipeline_foreach_layer (pipeline, _cogl_polygon_validate_layer_cb, &validate_state); pipeline = validate_state.pipeline; n_layers = cogl_pipeline_get_n_layers (pipeline); n_attributes = 1 + n_layers + (use_color ? 1 : 0); attributes = g_alloca (sizeof (CoglAttribute *) * n_attributes); /* Our data is arranged like: * [X, Y, Z, TX0, TY0, TX1, TY1..., R, G, B, A,...] */ stride = 3 + (2 * n_layers) + (use_color ? 1 : 0); stride_bytes = stride * sizeof (float); /* Make sure there is enough space in the global vertex array. This * is used so we can render the polygon with a single call to OpenGL * but still support any number of vertices */ g_array_set_size (ctx->polygon_vertices, n_vertices * stride); attribute_buffer = cogl_attribute_buffer_new (ctx, n_vertices * stride_bytes, NULL); attributes[0] = cogl_attribute_new (attribute_buffer, "cogl_position_in", stride_bytes, 0, 3, COGL_ATTRIBUTE_TYPE_FLOAT); for (i = 0; i < n_layers; i++) { static const char *names[] = { "cogl_tex_coord0_in", "cogl_tex_coord1_in", "cogl_tex_coord2_in", "cogl_tex_coord3_in", "cogl_tex_coord4_in", "cogl_tex_coord5_in", "cogl_tex_coord6_in", "cogl_tex_coord7_in" }; char *allocated_name = NULL; const char *name; if (i < 8) name = names[i]; else name = allocated_name = g_strdup_printf ("cogl_tex_coord%d_in", i); attributes[i + 1] = cogl_attribute_new (attribute_buffer, name, stride_bytes, /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */ 12 + 8 * i, 2, COGL_ATTRIBUTE_TYPE_FLOAT); g_free (allocated_name); } if (use_color) { attributes[n_attributes - 1] = cogl_attribute_new (attribute_buffer, "cogl_color_in", stride_bytes, /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */ 12 + 8 * n_layers, 4, COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); } /* Convert the vertices into an array of float vertex attributes */ v = (float *)ctx->polygon_vertices->data; for (i = 0; i < n_vertices; i++) { AppendTexCoordsState append_tex_coords_state; uint8_t *c; /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */ v[0] = vertices[i].x; v[1] = vertices[i].y; v[2] = vertices[i].z; append_tex_coords_state.vertices_in = vertices; append_tex_coords_state.vertex = i; append_tex_coords_state.layer = 0; append_tex_coords_state.vertices_out = v; cogl_pipeline_foreach_layer (pipeline, append_tex_coord_attributes_cb, &append_tex_coords_state); if (use_color) { /* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */ c = (uint8_t *) (v + 3 + 2 * n_layers); c[0] = cogl_color_get_red_byte (&vertices[i].color); c[1] = cogl_color_get_green_byte (&vertices[i].color); c[2] = cogl_color_get_blue_byte (&vertices[i].color); c[3] = cogl_color_get_alpha_byte (&vertices[i].color); } v += stride; } v = (float *)ctx->polygon_vertices->data; cogl_buffer_set_data (COGL_BUFFER (attribute_buffer), 0, v, ctx->polygon_vertices->len * sizeof (float)); /* XXX: although this may seem redundant, we need to do this since * cogl_polygon() can be used with legacy state and its the source stack * which track whether legacy state is enabled. * * (We only have a CoglDrawFlag to disable legacy state not one * to enable it) */ cogl_push_source (pipeline); _cogl_framebuffer_draw_attributes (cogl_get_draw_framebuffer (), pipeline, COGL_VERTICES_MODE_TRIANGLE_FAN, 0, n_vertices, attributes, n_attributes, 0 /* no draw flags */); cogl_pop_source (); if (pipeline != validate_state.original_pipeline) cogl_object_unref (pipeline); cogl_object_unref (attribute_buffer); for (i = 0; i < n_attributes; i++) cogl_object_unref (attributes[i]); }
static void clutter_canvas_emit_draw (ClutterCanvas *self) { ClutterCanvasPrivate *priv = self->priv; int real_width, real_height; cairo_surface_t *surface; gboolean mapped_buffer; unsigned char *data; CoglBuffer *buffer; int window_scale = 1; gboolean res; cairo_t *cr; g_assert (priv->height > 0 && priv->width > 0); priv->dirty = TRUE; if (priv->scale_factor_set) window_scale = priv->scale_factor; else g_object_get (clutter_settings_get_default (), "window-scaling-factor", &window_scale, NULL); real_width = priv->width * window_scale; real_height = priv->height * window_scale; CLUTTER_NOTE (MISC, "Creating Cairo surface with size %d x %d (real: %d x %d, scale: %d)", priv->width, priv->height, real_width, real_height, window_scale); if (priv->buffer == NULL) { CoglContext *ctx; ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); priv->buffer = cogl_bitmap_new_with_size (ctx, real_width, real_height, CLUTTER_CAIRO_FORMAT_ARGB32); } buffer = COGL_BUFFER (cogl_bitmap_get_buffer (priv->buffer)); if (buffer == NULL) return; cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_DYNAMIC); data = cogl_buffer_map (buffer, COGL_BUFFER_ACCESS_READ_WRITE, COGL_BUFFER_MAP_HINT_DISCARD); if (data != NULL) { int bitmap_stride = cogl_bitmap_get_rowstride (priv->buffer); surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, real_width, real_height, bitmap_stride); mapped_buffer = TRUE; } else { surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, real_width, real_height); mapped_buffer = FALSE; } cairo_surface_set_device_scale (surface, window_scale, window_scale); self->priv->cr = cr = cairo_create (surface); g_signal_emit (self, canvas_signals[DRAW], 0, cr, priv->width, priv->height, &res); #ifdef CLUTTER_ENABLE_DEBUG if (_clutter_diagnostic_enabled () && cairo_status (cr)) { g_warning ("Drawing failed for <ClutterCanvas>[%p]: %s", self, cairo_status_to_string (cairo_status (cr))); } #endif self->priv->cr = NULL; cairo_destroy (cr); if (mapped_buffer) cogl_buffer_unmap (buffer); else { int size = cairo_image_surface_get_stride (surface) * priv->height; cogl_buffer_set_data (buffer, 0, cairo_image_surface_get_data (surface), size); } cairo_surface_destroy (surface); }