static void emit_vertex_buffer_geometry (CoglPangoDisplayListNode *node) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* 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.vertex_buffer == COGL_INVALID_HANDLE) { CoglHandle vb = cogl_vertex_buffer_new (node->d.texture.verts->len); cogl_vertex_buffer_add (vb, "gl_Vertex", 2, COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, sizeof (CoglPangoDisplayListVertex), &g_array_index (node->d.texture.verts, CoglPangoDisplayListVertex, 0).x); cogl_vertex_buffer_add (vb, "gl_MultiTexCoord0", 2, COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, sizeof (CoglPangoDisplayListVertex), &g_array_index (node->d.texture.verts, CoglPangoDisplayListVertex, 0).t_x); cogl_vertex_buffer_submit (vb); node->d.texture.vertex_buffer = vb; } #ifdef CLUTTER_COGL_HAS_GL if (ctx->driver == COGL_DRIVER_GL) cogl_vertex_buffer_draw (node->d.texture.vertex_buffer, GL_QUADS, 0, node->d.texture.verts->len); else #endif { /* GLES doesn't support GL_QUADS so instead we use a VBO with indexed vertices to generate GL_TRIANGLES from the quads */ int n_indices = node->d.texture.verts->len / 4 * 6; CoglHandle indices_vbo = cogl_vertex_buffer_indices_get_for_quads (n_indices); cogl_vertex_buffer_draw_elements (node->d.texture.vertex_buffer, COGL_VERTICES_MODE_TRIANGLES, indices_vbo, 0, node->d.texture.verts->len - 1, 0, n_indices); } }
static void init_quad_mesh (TestState *state) { int x, y; float *vert; guint8 *color; /* Note: we maintain the minimum number of vertices possible. This minimizes * the work required when we come to morph the geometry. * * We use static indices into our mesh so that we can treat the data like a * single triangle list and drawing can be done in one operation (Note: We * are using degenerate triangles at the edges to link to the next row) */ state->quad_mesh_verts = g_malloc0 (sizeof (float) * 3 * (MESH_WIDTH + 1) * (MESH_HEIGHT + 1)); state->quad_mesh_colors = g_malloc0 (sizeof (guint8) * 4 * (MESH_WIDTH + 1) * (MESH_HEIGHT + 1)); vert = state->quad_mesh_verts; color = state->quad_mesh_colors; for (y = 0; y <= MESH_HEIGHT; y++) for (x = 0; x <= MESH_WIDTH; x++) { vert[0] = x * QUAD_WIDTH; vert[1] = y * QUAD_HEIGHT; vert += 3; color[3] = gaussian (x * QUAD_WIDTH, y * QUAD_HEIGHT) * 255.0; color += 4; } state->buffer = cogl_vertex_buffer_new ((MESH_WIDTH + 1)*(MESH_HEIGHT + 1)); cogl_vertex_buffer_add (state->buffer, "gl_Vertex", 3, /* n components */ COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, /* normalized */ 0, /* stride */ state->quad_mesh_verts); cogl_vertex_buffer_add (state->buffer, "gl_Color", 4, /* n components */ COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE, FALSE, /* normalized */ 0, /* stride */ state->quad_mesh_colors); cogl_vertex_buffer_submit (state->buffer); init_static_index_arrays (state); }
static void _clutter_stage_wayland_repair_dirty(ClutterStageWayland *stage_wayland, ClutterStage *stage) { CoglMaterial *outline = NULL; CoglHandle vbo; float vertices[8], texcoords[8]; CoglMatrix modelview; cairo_region_t *dirty; cairo_rectangle_int_t rect; int i, count; float width, height; dirty = stage_wayland->back_buffer->dirty_region; stage_wayland->back_buffer->dirty_region = NULL; cairo_region_subtract (dirty, stage_wayland->repaint_region); width = stage_wayland->allocation.width; height = stage_wayland->allocation.height; /* If this is the first time we render, there is no front buffer to * copy back from, but then the dirty region not covered by the * repaint should be empty, because we repaint the entire stage. * * assert(stage_wayland->front_buffer != NULL) || * cairo_region_is_empty(dirty); * * FIXME: in test-rotate, the stage never queues a full repaint * initially, it's restricted to the paint box of it's rotating * children. */ if (!stage_wayland->front_buffer) return; outline = cogl_material_new (); cogl_material_set_layer (outline, 0, stage_wayland->front_buffer->tex); count = cairo_region_num_rectangles (dirty); for (i = 0; i < count; i++) { cairo_region_get_rectangle (dirty, i, &rect); vbo = cogl_vertex_buffer_new (4); vertices[0] = rect.x - 1; vertices[1] = rect.y - 1; vertices[2] = rect.x + rect.width + 1; vertices[3] = rect.y - 1; vertices[4] = rect.x + rect.width + 1; vertices[5] = rect.y + rect.height + 1; vertices[6] = rect.x - 1; vertices[7] = rect.y + rect.height + 1; cogl_vertex_buffer_add (vbo, "gl_Vertex", 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, /* normalized */ 0, /* stride */ vertices); texcoords[0] = vertices[0] / width; texcoords[1] = vertices[1] / height; texcoords[2] = vertices[2] / width; texcoords[3] = vertices[3] / height; texcoords[4] = vertices[4] / width; texcoords[5] = vertices[5] / height; texcoords[6] = vertices[6] / width; texcoords[7] = vertices[7] / height; cogl_vertex_buffer_add (vbo, "gl_MultiTexCoord0", 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, /* normalized */ 0, /* stride */ texcoords); cogl_vertex_buffer_submit (vbo); cogl_push_matrix (); cogl_matrix_init_identity (&modelview); _clutter_actor_apply_modelview_transform (CLUTTER_ACTOR (stage), &modelview); cogl_set_modelview_matrix (&modelview); cogl_set_source (outline); cogl_vertex_buffer_draw (vbo, COGL_VERTICES_MODE_TRIANGLE_FAN, 0 , 4); cogl_pop_matrix (); cogl_object_unref (vbo); } cairo_region_destroy (dirty); }
static void frame_cb (ClutterTimeline *timeline, gint elapsed_msecs, TestState *state) { guint x, y; float period_progress = clutter_timeline_get_progress (timeline); float period_progress_sin = sinf (period_progress); float wave_shift = period_progress * WAVE_SPEED; float ripple_shift = period_progress * RIPPLE_SPEED; for (y = 0; y <= MESH_HEIGHT; y++) for (x = 0; x <= MESH_WIDTH; x++) { guint vert_index = (MESH_WIDTH + 1) * y + x; float *vert = &state->quad_mesh_verts[3 * vert_index]; float real_x = x * QUAD_WIDTH; float real_y = y * QUAD_HEIGHT; float wave_offset = (float)x / (MESH_WIDTH + 1); float wave_angle = (WAVE_PERIODS * 2 * G_PI * wave_offset) + wave_shift; float wave_sin = sinf (wave_angle); float a_sqr = (RIPPLE_CENTER_X - real_x) * (RIPPLE_CENTER_X - real_x); float b_sqr = (RIPPLE_CENTER_Y - real_y) * (RIPPLE_CENTER_Y - real_y); float ripple_offset = sqrtf (a_sqr + b_sqr) / RIPPLE_RADIUS; float ripple_angle = (RIPPLE_PERIODS * 2 * G_PI * ripple_offset) + ripple_shift; float ripple_sin = sinf (ripple_angle); float h, s, l; guint8 *color; vert[2] = (wave_sin * WAVE_DEPTH) + (ripple_sin * RIPPLE_DEPTH); /* Burn some CPU time picking a pretty color... */ h = (HSL_OFFSET + wave_sin + ripple_sin + period_progress_sin) * HSL_SCALE; s = 0.5; l = 0.25 + (period_progress_sin + 1.0) / 4.0; color = &state->quad_mesh_colors[4 * vert_index]; /* A bit of a sneaky cast, but it seems safe to assume the ClutterColor * typedef is set in stone... */ clutter_color_from_hls ((ClutterColor *)color, h * 360.0, l, s); color[0] = (color[0] * color[3] + 128) / 255; color[1] = (color[1] * color[3] + 128) / 255; color[2] = (color[2] * color[3] + 128) / 255; } cogl_vertex_buffer_add (state->buffer, "gl_Vertex", 3, /* n components */ COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, /* normalized */ 0, /* stride */ state->quad_mesh_verts); cogl_vertex_buffer_add (state->buffer, "gl_Color", 4, /* n components */ COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE, FALSE, /* normalized */ 0, /* stride */ state->quad_mesh_colors); cogl_vertex_buffer_submit (state->buffer); clutter_actor_set_rotation (state->dummy, CLUTTER_Z_AXIS, 360 * period_progress, (MESH_WIDTH * QUAD_WIDTH) / 2, (MESH_HEIGHT * QUAD_HEIGHT) / 2, 0); clutter_actor_set_rotation (state->dummy, CLUTTER_X_AXIS, 360 * period_progress, (MESH_WIDTH * QUAD_WIDTH) / 2, (MESH_HEIGHT * QUAD_HEIGHT) / 2, 0); }
void test_cogl_vertex_buffer_contiguous (TestUtilsGTestFixture *fixture, void *data) { TestState state; ClutterActor *stage; ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff}; ClutterActor *group; unsigned int idle_source; guchar tex_data[] = { 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff }; stage = clutter_stage_get_default (); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_clr); clutter_actor_get_geometry (stage, &state.stage_geom); group = clutter_group_new (); clutter_actor_set_size (group, state.stage_geom.width, state.stage_geom.height); clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); /* We force continuous redrawing incase someone comments out the * clutter_main_quit and wants visual feedback for the test since we * wont be doing anything else that will trigger redrawing. */ idle_source = g_idle_add (queue_redraw, stage); g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); state.texture = cogl_texture_new_from_data (2, 2, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_RGBA_8888, COGL_PIXEL_FORMAT_ANY, 0, /* auto calc row stride */ tex_data); state.material = cogl_material_new (); cogl_material_set_color4ub (state.material, 0x00, 0xff, 0x00, 0xff); cogl_material_set_layer (state.material, 0, state.texture); { GLfloat triangle_verts[3][2] = { {0.0, 0.0}, {100.0, 100.0}, {0.0, 100.0} }; GLbyte triangle_colors[3][4] = { {0x00, 0x00, 0xff, 0xff}, /* blue */ {0x00, 0x00, 0xff, 0x00}, /* transparent blue */ {0x00, 0x00, 0xff, 0x00} /* transparent blue */ }; GLfloat triangle_tex_coords[3][2] = { {0.0, 0.0}, {1.0, 1.0}, {0.0, 1.0} }; state.buffer = cogl_vertex_buffer_new (3 /* n vertices */); cogl_vertex_buffer_add (state.buffer, "gl_Vertex", 2, /* n components */ GL_FLOAT, FALSE, /* normalized */ 0, /* stride */ triangle_verts); cogl_vertex_buffer_add (state.buffer, "gl_Color::blue", 4, /* n components */ GL_UNSIGNED_BYTE, FALSE, /* normalized */ 0, /* stride */ triangle_colors); cogl_vertex_buffer_add (state.buffer, "gl_MultiTexCoord0", 2, /* n components */ GL_FLOAT, FALSE, /* normalized */ 0, /* stride */ triangle_tex_coords); cogl_vertex_buffer_submit (state.buffer); } clutter_actor_show_all (stage); clutter_main (); cogl_handle_unref (state.buffer); cogl_handle_unref (state.material); cogl_handle_unref (state.texture); g_source_remove (idle_source); if (g_test_verbose ()) g_print ("OK\n"); }
static void clutter_stage_glx_redraw (ClutterStageWindow *stage_window) { ClutterBackendX11 *backend_x11; ClutterBackendGLX *backend_glx; ClutterStageX11 *stage_x11; ClutterStageGLX *stage_glx; GLXDrawable drawable; unsigned int video_sync_count; gboolean may_use_clipped_redraw; gboolean use_clipped_redraw; CLUTTER_STATIC_TIMER (painting_timer, "Redrawing", /* parent */ "Painting actors", "The time spent painting actors", 0 /* no application private data */); CLUTTER_STATIC_TIMER (swapbuffers_timer, "Redrawing", /* parent */ "glXSwapBuffers", "The time spent blocked by glXSwapBuffers", 0 /* no application private data */); CLUTTER_STATIC_TIMER (blit_sub_buffer_timer, "Redrawing", /* parent */ "glx_blit_sub_buffer", "The time spent in _glx_blit_sub_buffer", 0 /* no application private data */); stage_x11 = CLUTTER_STAGE_X11 (stage_window); if (stage_x11->xwin == None) return; stage_glx = CLUTTER_STAGE_GLX (stage_window); backend_x11 = stage_x11->backend; backend_glx = CLUTTER_BACKEND_GLX (backend_x11); CLUTTER_TIMER_START (_clutter_uprof_context, painting_timer); if (G_LIKELY (backend_glx->can_blit_sub_buffer) && /* NB: a zero width redraw clip == full stage redraw */ stage_glx->bounding_redraw_clip.width != 0 && /* some drivers struggle to get going and produce some junk * frames when starting up... */ G_LIKELY (stage_glx->frame_count > 3) && /* While resizing a window clipped redraws are disabled to avoid * artefacts. See clutter-event-x11.c:event_translate for a * detailed explanation */ G_LIKELY (stage_x11->clipped_redraws_cool_off == 0)) { may_use_clipped_redraw = TRUE; } else may_use_clipped_redraw = FALSE; if (may_use_clipped_redraw && G_LIKELY (!(clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS))) use_clipped_redraw = TRUE; else use_clipped_redraw = FALSE; if (use_clipped_redraw) { CLUTTER_NOTE (CLIPPING, "Stage clip pushed: x=%d, y=%d, width=%d, height=%d\n", stage_glx->bounding_redraw_clip.x, stage_glx->bounding_redraw_clip.y, stage_glx->bounding_redraw_clip.width, stage_glx->bounding_redraw_clip.height); cogl_clip_push_window_rectangle (stage_glx->bounding_redraw_clip.x, stage_glx->bounding_redraw_clip.y, stage_glx->bounding_redraw_clip.width, stage_glx->bounding_redraw_clip.height); _clutter_stage_do_paint (stage_x11->wrapper, &stage_glx->bounding_redraw_clip); cogl_clip_pop (); } else { CLUTTER_NOTE (CLIPPING, "Unclipped stage paint\n"); _clutter_stage_do_paint (stage_x11->wrapper, NULL); } if (may_use_clipped_redraw && G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS))) { static CoglMaterial *outline = NULL; ClutterGeometry *clip = &stage_glx->bounding_redraw_clip; ClutterActor *actor = CLUTTER_ACTOR (stage_x11->wrapper); CoglHandle vbo; float x_1 = clip->x; float x_2 = clip->x + clip->width; float y_1 = clip->y; float y_2 = clip->y + clip->height; float quad[8] = { x_1, y_1, x_2, y_1, x_2, y_2, x_1, y_2 }; CoglMatrix modelview; if (outline == NULL) { outline = cogl_material_new (); cogl_material_set_color4ub (outline, 0xff, 0x00, 0x00, 0xff); } vbo = cogl_vertex_buffer_new (4); cogl_vertex_buffer_add (vbo, "gl_Vertex", 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, /* normalized */ 0, /* stride */ quad); cogl_vertex_buffer_submit (vbo); cogl_push_matrix (); cogl_matrix_init_identity (&modelview); _clutter_actor_apply_modelview_transform (actor, &modelview); cogl_set_modelview_matrix (&modelview); cogl_set_source (outline); cogl_vertex_buffer_draw (vbo, COGL_VERTICES_MODE_LINE_LOOP, 0 , 4); cogl_pop_matrix (); cogl_object_unref (vbo); } cogl_flush (); CLUTTER_TIMER_STOP (_clutter_uprof_context, painting_timer); drawable = stage_glx->glxwin ? stage_glx->glxwin : stage_x11->xwin; /* If we might ever use _clutter_backend_glx_blit_sub_buffer then we * always need to keep track of the video_sync_count so that we can * throttle blits. * * Note: we get the count *before* we issue any glXCopySubBuffer or * blit_sub_buffer request in case the count would go up before * returning control to us. */ if (backend_glx->can_blit_sub_buffer && backend_glx->get_video_sync) backend_glx->get_video_sync (&video_sync_count); /* push on the screen */ if (use_clipped_redraw) { ClutterGeometry *clip = &stage_glx->bounding_redraw_clip; ClutterGeometry copy_area; ClutterActor *actor; CLUTTER_NOTE (BACKEND, "_glx_blit_sub_buffer (window: 0x%lx, " "x: %d, y: %d, " "width: %d, height: %d)", (unsigned long) drawable, stage_glx->bounding_redraw_clip.x, stage_glx->bounding_redraw_clip.y, stage_glx->bounding_redraw_clip.width, stage_glx->bounding_redraw_clip.height); /* XXX: It seems there will be a race here in that the stage * window may be resized before glXCopySubBufferMESA is handled * and so we may copy the wrong region. I can't really see how * we can handle this with the current state of X but at least * in this case a full redraw should be queued by the resize * anyway so it should only exhibit temporary artefacts. */ actor = CLUTTER_ACTOR (stage_x11->wrapper); copy_area.y = clutter_actor_get_height (actor) - clip->y - clip->height; copy_area.x = clip->x; copy_area.width = clip->width; copy_area.height = clip->height; /* glXCopySubBufferMESA and glBlitFramebuffer are not integrated * with the glXSwapIntervalSGI mechanism which we usually use to * throttle the Clutter framerate to the vertical refresh and so * we have to manually wait for the vblank period... */ /* Here 'is_synchronized' only means that the blit won't cause a * tear, ie it won't prevent multiple blits per retrace if they * can all be performed in the blanking period. If that's the * case then we still want to use the vblank sync menchanism but * we only need it to throttle redraws. */ if (!backend_glx->blit_sub_buffer_is_synchronized) { /* XXX: note that glXCopySubBuffer, at least for Intel, is * synchronized with the vblank but glBlitFramebuffer may * not be so we use the same scheme we do when calling * glXSwapBuffers without the swap_control extension and * call glFinish () before waiting for the vblank period. * * See where we call glXSwapBuffers for more details. */ glFinish (); wait_for_vblank (backend_glx); } else if (backend_glx->get_video_sync) { /* If we have the GLX_SGI_video_sync extension then we can * be a bit smarter about how we throttle blits by avoiding * any waits if we can see that the video sync count has * already progressed. */ if (backend_glx->last_video_sync_count == video_sync_count) wait_for_vblank (backend_glx); } else wait_for_vblank (backend_glx); CLUTTER_TIMER_START (_clutter_uprof_context, blit_sub_buffer_timer); _clutter_backend_glx_blit_sub_buffer (backend_glx, drawable, copy_area.x, copy_area.y, copy_area.width, copy_area.height); CLUTTER_TIMER_STOP (_clutter_uprof_context, blit_sub_buffer_timer); } else { CLUTTER_NOTE (BACKEND, "glXSwapBuffers (display: %p, window: 0x%lx)", backend_x11->xdpy, (unsigned long) drawable); /* If we have GLX swap buffer events then glXSwapBuffers will return * immediately and we need to track that there is a swap in * progress... */ if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS)) stage_glx->pending_swaps++; if (backend_glx->vblank_type != CLUTTER_VBLANK_GLX_SWAP && backend_glx->vblank_type != CLUTTER_VBLANK_NONE) { /* If we are going to wait for VBLANK manually, we not only * need to flush out pending drawing to the GPU before we * sleep, we need to wait for it to finish. Otherwise, we * may end up with the situation: * * - We finish drawing - GPU drawing continues * - We go to sleep - GPU drawing continues * VBLANK - We call glXSwapBuffers - GPU drawing continues * - GPU drawing continues * - Swap buffers happens * * Producing a tear. Calling glFinish() first will cause us * to properly wait for the next VBLANK before we swap. This * obviously does not happen when we use _GLX_SWAP and let * the driver do the right thing */ glFinish (); wait_for_vblank (backend_glx); } CLUTTER_TIMER_START (_clutter_uprof_context, swapbuffers_timer); glXSwapBuffers (backend_x11->xdpy, drawable); CLUTTER_TIMER_STOP (_clutter_uprof_context, swapbuffers_timer); _cogl_swap_buffers_notify (); } backend_glx->last_video_sync_count = video_sync_count; /* reset the redraw clipping for the next paint... */ stage_glx->initialized_redraw_clip = FALSE; stage_glx->frame_count++; }
void test_cogl_vertex_buffer_interleved (TestConformSimpleFixture *fixture, gconstpointer data) { TestState state; ClutterActor *stage; ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff}; ClutterActor *group; guint idle_source; state.frame = 0; stage = clutter_stage_get_default (); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_clr); clutter_actor_get_geometry (stage, &state.stage_geom); group = clutter_group_new (); clutter_actor_set_size (group, state.stage_geom.width, state.stage_geom.height); clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); /* We force continuous redrawing of the stage, since we need to skip * the first few frames, and we wont be doing anything else that * will trigger redrawing. */ idle_source = g_idle_add (queue_redraw, stage); g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); { InterlevedVertex verts[3] = { { /* .x = */ 0.0, /* .y = */ 0.0, /* blue */ /* .r = */ 0x00, /* .g = */ 0x00, /* .b = */ 0xff, /* .a = */ 0xff }, { /* .x = */ 100.0, /* .y = */ 100.0, /* transparent blue */ /* .r = */ 0x00, /* .g = */ 0x00, /* .b = */ 0xff, /* .a = */ 0x00 }, { /* .x = */ 0.0, /* .y = */ 100.0, /* transparent blue */ /* .r = */ 0x00, /* .g = */ 0x00, /* .b = */ 0xff, /* .a = */ 0x00 }, }; /* We assume the compiler is doing no funny struct padding for this test: */ g_assert (sizeof (InterlevedVertex) == 12); state.buffer = cogl_vertex_buffer_new (3 /* n vertices */); cogl_vertex_buffer_add (state.buffer, "gl_Vertex", 2, /* n components */ GL_FLOAT, FALSE, /* normalized */ 12, /* stride */ &verts[0].x); cogl_vertex_buffer_add (state.buffer, "gl_Color", 4, /* n components */ GL_UNSIGNED_BYTE, FALSE, /* normalized */ 12, /* stride */ &verts[0].r); cogl_vertex_buffer_submit (state.buffer); } clutter_actor_show_all (stage); clutter_main (); cogl_handle_unref (state.buffer); g_source_remove (idle_source); if (g_test_verbose ()) g_print ("OK\n"); }
static void clutter_deform_effect_paint_target (ClutterOffscreenEffect *effect) { ClutterDeformEffect *self= CLUTTER_DEFORM_EFFECT (effect); ClutterDeformEffectPrivate *priv = self->priv; gboolean is_depth_enabled, is_cull_enabled; CoglHandle material; gint n_tiles; if (priv->is_dirty) { 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_size (effect, &width, &height)) clutter_actor_get_size (actor, &width, &height); for (i = 0; i < priv->y_tiles + 1; i++) { for (j = 0; j < priv->x_tiles + 1; j++) { CoglTextureVertex *vertex; vertex = &priv->vertices[(i * (priv->x_tiles + 1)) + j]; 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); } } /* XXX in theory, 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 */ cogl_vertex_buffer_add (priv->vbo, "gl_Vertex", 3, COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, sizeof (CoglTextureVertex), &priv->vertices->x); cogl_vertex_buffer_add (priv->vbo, "gl_MultiTexCoord0", 2, COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, sizeof (CoglTextureVertex), &priv->vertices->tx); cogl_vertex_buffer_add (priv->vbo, "gl_Color", 4, COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE, FALSE, sizeof (CoglTextureVertex), &priv->vertices->color); priv->is_dirty = FALSE; } /* enable depth test, if it's not already enabled */ is_depth_enabled = cogl_get_depth_test_enabled (); if (!is_depth_enabled) cogl_set_depth_test_enabled (TRUE); /* enable backface culling if it's not already enabled and if * we have a back material */ is_cull_enabled = cogl_get_backface_culling_enabled (); if (priv->back_material != COGL_INVALID_HANDLE && !is_cull_enabled) cogl_set_backface_culling_enabled (TRUE); else if (priv->back_material == COGL_INVALID_HANDLE && is_cull_enabled) cogl_set_backface_culling_enabled (FALSE); n_tiles = (priv->x_tiles + 1) * (priv->y_tiles + 1); /* draw the front */ material = clutter_offscreen_effect_get_target (effect); if (material != COGL_INVALID_HANDLE) { cogl_set_source (material); cogl_vertex_buffer_draw_elements (priv->vbo, COGL_VERTICES_MODE_TRIANGLE_STRIP, priv->indices, 0, n_tiles, 0, priv->n_indices); } /* draw the back */ material = priv->back_material; if (material != COGL_INVALID_HANDLE) { cogl_set_source (priv->back_material); cogl_vertex_buffer_draw_elements (priv->vbo, COGL_VERTICES_MODE_TRIANGLE_STRIP, priv->back_indices, 0, n_tiles, 0, priv->n_indices); } /* restore the previous state */ if (!is_depth_enabled) cogl_set_depth_test_enabled (FALSE); if (priv->back_material != COGL_INVALID_HANDLE && !is_cull_enabled) cogl_set_backface_culling_enabled (FALSE); else if (priv->back_material == COGL_INVALID_HANDLE && is_cull_enabled) cogl_set_backface_culling_enabled (TRUE); }
static void mx_fade_effect_update_vbo (MxFadeEffect *self) { guint n_quads; gint bu, br, bb, bl; gfloat x1, y1, x2, y2; CoglColor opaque, color; CoglTextureVertex verts[9*4]; MxFadeEffectPrivate *priv = self->priv; cogl_color_init_from_4ub (&opaque, 0xff, 0xff, 0xff, 0xff); cogl_color_init_from_4ub (&color, priv->color.red, priv->color.green, priv->color.blue, priv->color.alpha); /* Validate the bounds */ x1 = priv->x; y1 = priv->y; x2 = x1 + (priv->bounds_width ? priv->bounds_width : priv->width); y2 = y1 + (priv->bounds_height ? priv->bounds_height : priv->height); if (x1 < 0) x1 = 0; if (x2 > priv->width) x2 = priv->width; if (y1 < 0) y1 = 0; if (y2 > priv->height) y2 = priv->height; /* Validate the border sizes */ /* Note, * bu = Border-up * br = Border-right * bb = Border-bottom * bl = Border-left */ bu = priv->border[0]; br = priv->border[1]; bb = priv->border[2]; bl = priv->border[3]; if (y1 + bu >= y2) bu = y2 - y1 - 1; if (x1 + bl >= x2) bl = x2 - x1 - 1; if (x2 - br <= x1 + bl) br = x2 - (x1 + bl) - 1; if (y2 - bb <= y1 + bu) bb = y2 - (y1 + bu) - 1; n_quads = 0; /* Generate the top-left square */ if (bl && bu) { mx_fade_effect_draw_rect (&verts[n_quads*4], x1, y1, x1 + bl, y1 + bu, priv->width, priv->height, &color, &color, &opaque, &color, FALSE); n_quads ++; } /* Generate the top-middle square */ if (bu) { mx_fade_effect_draw_rect (&verts[n_quads*4], x1 + bl, y1, x2 - br, y1 + bu, priv->width, priv->height, &color, &color, &opaque, &opaque, FALSE); n_quads ++; } /* Generate the top-right square */ if (br && bu) { mx_fade_effect_draw_rect (&verts[n_quads*4], x2 - br, y1, x2, y1 + bu, priv->width, priv->height, &color, &color, &color, &opaque, TRUE); n_quads ++; } /* Generate the left square */ if (bl) { mx_fade_effect_draw_rect (&verts[n_quads*4], x1, y1 + bu, x1 + bl, y2 - bb, priv->width, priv->height, &color, &opaque, &opaque, &color, TRUE); n_quads ++; } /* Generate the middle square */ mx_fade_effect_draw_rect (&verts[n_quads*4], x1 + bl, y1 + bu, x2 - br, y2 - bb, priv->width, priv->height, &opaque, &opaque, &opaque, &opaque, TRUE); n_quads ++; /* Generate the right square */ if (br) { mx_fade_effect_draw_rect (&verts[n_quads*4], x2 - br, y1 + bu, x2, y2 - bb, priv->width, priv->height, &opaque, &color, &color, &opaque, TRUE); n_quads ++; } /* Generate the bottom-left square */ if (bb && bl) { mx_fade_effect_draw_rect (&verts[n_quads*4], x1, y2 - bb, x1 + bl, y2, priv->width, priv->height, &color, &opaque, &color, &color, TRUE); n_quads ++; } /* Generate the bottom-middle square */ if (bb) { mx_fade_effect_draw_rect (&verts[n_quads*4], x1 + bl, y2 - bb, x2 - br, y2, priv->width, priv->height, &opaque, &opaque, &color, &color, FALSE); n_quads ++; } /* Generate the bottom-right square */ if (bb && br) { mx_fade_effect_draw_rect (&verts[n_quads*4], x2 - br, y2 - bb, x2, y2, priv->width, priv->height, &opaque, &color, &color, &color, FALSE); n_quads ++; } /* Unref the old vbo if it's a different size - otherwise we reuse it */ if (priv->vbo && (n_quads != priv->n_quads)) { cogl_handle_unref (priv->vbo); priv->vbo = NULL; } priv->n_quads = n_quads; if (!priv->vbo) { priv->vbo = cogl_vertex_buffer_new (n_quads * 4); if (!priv->vbo) return; priv->indices = cogl_vertex_buffer_indices_get_for_quads (n_quads * 6); if (!priv->indices) return; } cogl_vertex_buffer_add (priv->vbo, "gl_Vertex", 2, COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, sizeof (CoglTextureVertex), &(verts[0].x)); cogl_vertex_buffer_add (priv->vbo, "gl_MultiTexCoord0", 2, COGL_ATTRIBUTE_TYPE_FLOAT, FALSE, sizeof (CoglTextureVertex), &(verts[0].tx)); cogl_vertex_buffer_add (priv->vbo, "gl_Color", 4, COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE, FALSE, sizeof (CoglTextureVertex), &(verts[0].color)); cogl_vertex_buffer_submit (priv->vbo); priv->update_vbo = FALSE; }