Beispiel #1
0
void
rut_mesh_foreach_index(rut_mesh_t *mesh,
                       rut_mesh_vertex_callback_t callback,
                       void *user_data,
                       const char *first_attribute,
                       ...)
{
    va_list ap;
    int n_attributes = 0;
    uint8_t **bases;
    int *strides;
    bool ready;

    va_start(ap, first_attribute);
    do {
        n_attributes++;
    } while (va_arg(ap, const char *));
    va_end(ap);

    bases = c_alloca(sizeof(void *) * n_attributes);
    strides = c_alloca(sizeof(int) * n_attributes);

    va_start(ap, first_attribute);
    ready = collect_attribute_state(mesh, bases, strides, first_attribute, ap);
    va_end(ap);

    c_return_if_fail(ready);

    foreach_vertex(
        mesh, callback, user_data, true, bases, strides, n_attributes);
}
Beispiel #2
0
static void
_cg_winsys_onscreen_swap_region(cg_onscreen_t *onscreen,
                                const int *user_rectangles,
                                int n_rectangles)
{
    cg_device_t *dev = CG_FRAMEBUFFER(onscreen)->dev;
    cg_renderer_t *renderer = dev->display->renderer;
    cg_renderer_egl_t *egl_renderer = renderer->winsys;
    cg_onscreen_egl_t *egl_onscreen = onscreen->winsys;
    cg_framebuffer_t *framebuffer = CG_FRAMEBUFFER(onscreen);
    int framebuffer_height = cg_framebuffer_get_height(framebuffer);
    int *rectangles = c_alloca(sizeof(int) * n_rectangles * 4);
    int i;

    /* eglSwapBuffersRegion expects rectangles relative to the
     * bottom left corner but we are given rectangles relative to
     * the top left so we need to flip them... */
    memcpy(rectangles, user_rectangles, sizeof(int) * n_rectangles * 4);
    for (i = 0; i < n_rectangles; i++) {
        int *rect = &rectangles[4 * i];
        rect[1] = framebuffer_height - rect[1] - rect[3];
    }

    /* At least for eglSwapBuffers the EGL spec says that the surface to
       swap must be bound to the current context. It looks like Mesa
       also validates that this is the case for eglSwapBuffersRegion so
       we must bind here too */
    _cg_framebuffer_flush_state(CG_FRAMEBUFFER(onscreen),
                                CG_FRAMEBUFFER(onscreen),
                                CG_FRAMEBUFFER_STATE_BIND);

    if (!egl_renderer->pf_eglSwapBuffersRegion(egl_renderer->edpy,
                                               egl_onscreen->egl_surface,
                                               n_rectangles,
                                               rectangles))
        c_warning("Error reported by eglSwapBuffersRegion");
}
Beispiel #3
0
rut_mesh_t *
rut_mesh_copy(rut_mesh_t *mesh)
{
    rut_buffer_t **buffers;
    int n_buffers = 0;
    rut_buffer_t **attribute_buffers;
    rut_buffer_t **attribute_buffers_map;
    rut_attribute_t **attributes;
    rut_mesh_t *copy;
    int i;

    buffers = c_alloca(sizeof(void *) * mesh->n_attributes);
    attribute_buffers = c_alloca(sizeof(void *) * mesh->n_attributes);
    attribute_buffers_map = c_alloca(sizeof(void *) * mesh->n_attributes);

    /* NB:
     * attributes may refer to shared buffers so we need to first
     * figure out how many unique buffers the mesh refers too...
     */

    for (i = 0; i < mesh->n_attributes; i++) {
        int j;

        for (j = 0; i < n_buffers; j++)
            if (buffers[j] == mesh->attributes[i]->buffered.buffer)
                break;

        if (j < n_buffers)
            attribute_buffers_map[i] = attribute_buffers[j];
        else {
            attribute_buffers[n_buffers] =
                rut_buffer_new(mesh->attributes[i]->buffered.buffer->size);
            memcpy(attribute_buffers[n_buffers]->data,
                   mesh->attributes[i]->buffered.buffer->data,
                   mesh->attributes[i]->buffered.buffer->size);

            attribute_buffers_map[i] = attribute_buffers[n_buffers];
            buffers[n_buffers++] = mesh->attributes[i]->buffered.buffer;
        }
    }

    attributes = c_alloca(sizeof(void *) * mesh->n_attributes);
    for (i = 0; i < mesh->n_attributes; i++) {
        if (mesh->attributes[i]->is_buffered) {
            attributes[i] = rut_attribute_new(attribute_buffers_map[i],
                                              mesh->attributes[i]->name,
                                              mesh->attributes[i]->buffered.stride,
                                              mesh->attributes[i]->buffered.offset,
                                              mesh->attributes[i]->buffered.n_components,
                                              mesh->attributes[i]->buffered.type);
        } else {
            attributes[i] = rut_attribute_new_const(mesh->attributes[i]->name,
                                                    mesh->attributes[i]->constant.n_components,
                                                    mesh->attributes[i]->constant.n_columns,
                                                    mesh->attributes[i]->constant.transpose,
                                                    mesh->attributes[i]->constant.value);
        }
        attributes[i]->normalized = mesh->attributes[i]->normalized;
        attributes[i]->instance_stride = mesh->attributes[i]->instance_stride;
    }

    copy = rut_mesh_new(mesh->mode, mesh->n_vertices,
                        attributes, mesh->n_attributes);

    for (i = 0; i < mesh->n_attributes; i++)
        rut_object_unref(attributes[i]);

    if (mesh->indices_buffer) {
        rut_buffer_t *indices_buffer =
            rut_buffer_new(mesh->indices_buffer->size);
        memcpy(indices_buffer->data,
               mesh->indices_buffer->data,
               mesh->indices_buffer->size);
        rut_mesh_set_indices(
            copy, mesh->indices_type, indices_buffer, mesh->n_indices);
        rut_object_unref(indices_buffer);
    }

    return copy;
}
Beispiel #4
0
cg_primitive_t *
rut_mesh_create_primitive(rut_shell_t *shell, rut_mesh_t *mesh)
{
    int n_attributes = mesh->n_attributes;
    rut_buffer_t **buffers;
    int n_buffers = 0;
    cg_attribute_buffer_t **attribute_buffers;
    cg_attribute_buffer_t **attribute_buffers_map;
    cg_attribute_t **attributes;
    cg_primitive_t *primitive;
    int i;

    buffers = c_alloca(sizeof(void *) * n_attributes);
    attribute_buffers = c_alloca(sizeof(void *) * n_attributes);
    attribute_buffers_map = c_alloca(sizeof(void *) * n_attributes);

    /* NB:
     * attributes may refer to shared buffers so we need to first
     * figure out how many unique buffers the mesh refers too...
     */

    for (i = 0; i < n_attributes; i++) {
        int j;

        if (!mesh->attributes[i]->is_buffered)
            continue;

        for (j = 0; i < n_buffers; j++)
            if (buffers[j] == mesh->attributes[i]->buffered.buffer)
                break;

        if (j < n_buffers)
            attribute_buffers_map[i] = attribute_buffers[j];
        else {
            attribute_buffers[n_buffers] =
                cg_attribute_buffer_new(shell->cg_device,
                                        mesh->attributes[i]->buffered.buffer->size,
                                        mesh->attributes[i]->buffered.buffer->data);

            attribute_buffers_map[i] = attribute_buffers[n_buffers];
            buffers[n_buffers++] = mesh->attributes[i]->buffered.buffer;
        }
    }

    attributes = c_alloca(sizeof(void *) * mesh->n_attributes);
    for (i = 0; i < n_attributes; i++) {
        if (mesh->attributes[i]->is_buffered) {
            cg_attribute_type_t type =
                get_cg_attribute_type(mesh->attributes[i]->buffered.type);

            attributes[i] = cg_attribute_new(attribute_buffers_map[i],
                                             mesh->attributes[i]->name,
                                             mesh->attributes[i]->buffered.stride,
                                             mesh->attributes[i]->buffered.offset,
                                             mesh->attributes[i]->buffered.n_components,
                                             type);
        } else {
            attributes[i] = cg_attribute_new_const(shell->cg_device,
                                                   mesh->attributes[i]->name,
                                                   mesh->attributes[i]->constant.n_components,
                                                   mesh->attributes[i]->constant.n_columns,
                                                   mesh->attributes[i]->constant.transpose,
                                                   mesh->attributes[i]->constant.value);
        }

        cg_attribute_set_normalized(attributes[i], mesh->attributes[i]->normalized);
        cg_attribute_set_instance_stride(attributes[i], mesh->attributes[i]->instance_stride);
    }

    for (i = 0; i < n_buffers; i++)
        cg_object_unref(attribute_buffers[i]);

    primitive = cg_primitive_new_with_attributes(
        mesh->mode, mesh->n_vertices, attributes, mesh->n_attributes);

    for (i = 0; i < n_attributes; i++)
        cg_object_unref(attributes[i]);

    if (mesh->indices_buffer) {
        cg_indices_t *indices = cg_indices_new(shell->cg_device,
                                               mesh->indices_type,
                                               mesh->indices_buffer->data,
                                               mesh->n_indices);
        cg_primitive_set_indices(primitive, indices, mesh->n_indices);
        cg_object_unref(indices);
    }

    return primitive;
}
Beispiel #5
0
void
rut_mesh_foreach_triangle(rut_mesh_t *mesh,
                          rut_mesh_triangle_callback_t callback,
                          void *user_data,
                          const char *first_attribute,
                          ...)
{
    va_list ap;
    int n_vertices;
    int n_attributes = 0;
    uint8_t **bases;
    int *strides;
    void **data0;
    void **data1;
    void **data2;
    void **tri_v[3];
    int tri_i[3];
    bool ready;

    switch (mesh->mode) {
    case CG_VERTICES_MODE_LINES:
    case CG_VERTICES_MODE_LINE_STRIP:
    case CG_VERTICES_MODE_LINE_LOOP:
        return;
    default:
        break;
    }

    n_vertices = mesh->indices_buffer ? mesh->n_indices : mesh->n_vertices;
    if (n_vertices < 3)
        return;

    va_start(ap, first_attribute);
    do {
        n_attributes++;
    } while (va_arg(ap, const char *));
    va_end(ap);

    bases = c_alloca(sizeof(void *) * n_attributes);
    strides = c_alloca(sizeof(int) * n_attributes);
    data0 = c_alloca(sizeof(void *) * n_attributes);
    data1 = c_alloca(sizeof(void *) * n_attributes);
    data2 = c_alloca(sizeof(void *) * n_attributes);

    tri_v[0] = data0;
    tri_v[1] = data1;
    tri_v[2] = data2;

    va_start(ap, first_attribute);
    ready = collect_attribute_state(mesh, bases, strides, first_attribute, ap);
    va_end(ap);

    c_return_if_fail(ready);

#define SWAP_TRIANGLE_VERTICES(V0, V1)                                         \
    do {                                                                       \
        void **tmp_v;                                                          \
        int tmp_i;                                                             \
        tmp_v = tri_v[V0];                                                     \
        tri_v[V0] = tri_v[V1];                                                 \
        tri_v[V1] = tmp_v;                                                     \
                                                                               \
        tmp_i = tri_i[V0];                                                     \
        tri_i[V0] = tri_i[V1];                                                 \
        tri_i[V1] = tmp_i;                                                     \
    } while (1)

    /* Make sure we don't overrun the vertices if we don't have a
     * multiple of three vertices in triangle list mode */
    if (mesh->mode == CG_VERTICES_MODE_TRIANGLES)
        n_vertices -= 2;

    if (mesh->indices_buffer) {
        index_state_t state;
        cg_vertices_mode_t mode = mesh->mode;
        int i = 0;

        state.n_attributes = n_attributes;
        state.bases = bases;
        state.strides = strides;
        state.indices_type = mesh->indices_type;
        state.indices = mesh->indices_buffer->data;

        tri_i[0] = move_to_i(i++, &state, tri_v[0]);
        tri_i[1] = move_to_i(i++, &state, tri_v[1]);
        tri_i[2] = move_to_i(i++, &state, tri_v[2]);

        while (true) {
            if (!callback(tri_v[0],
                          tri_v[1],
                          tri_v[2],
                          tri_i[0],
                          tri_i[1],
                          tri_i[2],
                          user_data))
                return;

            if (i >= n_vertices)
                break;

            switch (mode) {
            case CG_VERTICES_MODE_TRIANGLES:
                tri_i[0] = move_to_i(i++, &state, tri_v[0]);
                tri_i[1] = move_to_i(i++, &state, tri_v[1]);
                tri_i[2] = move_to_i(i++, &state, tri_v[2]);
                break;
            case CG_VERTICES_MODE_TRIANGLE_FAN:
                SWAP_TRIANGLE_VERTICES(1, 2);
                tri_i[2] = move_to_i(i++, &state, tri_v[2]);
                break;
            case CG_VERTICES_MODE_TRIANGLE_STRIP:
                SWAP_TRIANGLE_VERTICES(0, 1);
                SWAP_TRIANGLE_VERTICES(1, 2);
                tri_i[2] = move_to_i(i++, &state, tri_v[2]);
                break;
            default:
                c_warn_if_reached();
            }
        }
    } else {
        cg_vertices_mode_t mode = mesh->mode;
        int i = 0;

        tri_i[0] = move_to(i++, n_attributes, bases, strides, tri_v[0]);
        tri_i[1] = move_to(i++, n_attributes, bases, strides, tri_v[1]);
        tri_i[2] = move_to(i++, n_attributes, bases, strides, tri_v[2]);

        while (true) {
            if (!callback(tri_v[0],
                          tri_v[1],
                          tri_v[2],
                          tri_i[0],
                          tri_i[1],
                          tri_i[2],
                          user_data))
                return;

            if (i >= n_vertices)
                break;

            switch (mode) {
            case CG_VERTICES_MODE_TRIANGLES:
                tri_i[0] = move_to(i++, n_attributes, bases, strides, tri_v[0]);
                tri_i[1] = move_to(i++, n_attributes, bases, strides, tri_v[1]);
                tri_i[2] = move_to(i++, n_attributes, bases, strides, tri_v[2]);
                break;
            case CG_VERTICES_MODE_TRIANGLE_FAN:
                SWAP_TRIANGLE_VERTICES(1, 2);
                tri_i[2] = move_to(i++, n_attributes, bases, strides, tri_v[2]);
                break;
            case CG_VERTICES_MODE_TRIANGLE_STRIP:
                SWAP_TRIANGLE_VERTICES(0, 1);
                SWAP_TRIANGLE_VERTICES(1, 2);
                tri_i[2] = move_to(i++, n_attributes, bases, strides, tri_v[2]);
                break;
            default:
                c_warn_if_reached();
            }
        }
    }
#undef SWAP_TRIANGLE_VERTICES
}
Beispiel #6
0
static void
foreach_vertex(rut_mesh_t *mesh,
               rut_mesh_vertex_callback_t callback,
               void *user_data,
               bool ignore_indices,
               uint8_t **bases,
               int *strides,
               int n_attributes)
{
    if (mesh->indices_buffer && !ignore_indices) {
        void *indices_data = mesh->indices_buffer->data;
        cg_indices_type_t indices_type = mesh->indices_type;
        int n_indices = mesh->n_indices;
        void **data = c_alloca(sizeof(void *) * n_attributes);
        int i;

        switch (indices_type) {
        case CG_INDICES_TYPE_UNSIGNED_BYTE:

            for (i = 0; i < n_indices; i++) {
                int v = ((uint8_t *)indices_data)[i];
                int j;

                for (j = 0; j < n_attributes; j++)
                    data[j] = bases[j] + v * strides[j];

                callback(data, v, user_data);
            }

            break;
        case CG_INDICES_TYPE_UNSIGNED_SHORT:

            for (i = 0; i < n_indices; i++) {
                int v = ((uint16_t *)indices_data)[i];
                int j;

                for (j = 0; j < n_attributes; j++)
                    data[j] = bases[j] + v * strides[j];

                callback(data, v, user_data);
            }

            break;
        case CG_INDICES_TYPE_UNSIGNED_INT:

            for (i = 0; i < n_indices; i++) {
                int v = ((uint32_t *)indices_data)[i];
                int j;

                for (j = 0; j < n_attributes; j++)
                    data[j] = bases[j] + v * strides[j];

                callback(data, v, user_data);
            }

            break;
        }
    } else {
        int n_vertices = mesh->n_vertices;
        int i;

        for (i = 0; i < n_vertices; i++) {
            int j;

            callback((void **)bases, i, user_data);

            for (j = 0; j < n_attributes; j++)
                bases[j] += strides[j];
        }
    }
}
Beispiel #7
0
void
_cg_glsl_shader_set_source_with_boilerplate(cg_device_t *dev,
                                            GLuint shader_gl_handle,
                                            GLenum shader_gl_type,
                                            GLsizei count_in,
                                            const char **strings_in,
                                            const GLint *lengths_in)
{
    const char *vertex_boilerplate;
    const char *fragment_boilerplate;

    const char **strings = c_alloca(sizeof(char *) * (count_in + 6));
    GLint *lengths = c_alloca(sizeof(GLint) * (count_in + 6));
    char *version_string;
    int count = 0;

    vertex_boilerplate = _CG_VERTEX_SHADER_BOILERPLATE;
    fragment_boilerplate = _CG_FRAGMENT_SHADER_BOILERPLATE;

    version_string =
        c_strdup_printf("#version %i\n\n", dev->glsl_version_to_use);

    strings[count] = version_string;
    lengths[count++] = -1;

    if (_cg_has_private_feature(dev, CG_PRIVATE_FEATURE_GL_EMBEDDED) &&
        cg_has_feature(dev, CG_FEATURE_ID_TEXTURE_3D)) {
        static const char texture_3d_extension[] =
            "#extension GL_OES_texture_3D : enable\n";
        strings[count] = texture_3d_extension;
        lengths[count++] = sizeof(texture_3d_extension) - 1;
    }

    if (shader_gl_type == GL_VERTEX_SHADER) {
        if (dev->glsl_version_to_use < 130) {
            strings[count] = "#define in attribute\n"
                             "#define out varying\n";
            lengths[count++] = -1;
        } else {
            /* To support source compatibility with glsl >= 1.3 which has
             * replaced
             * all of the texture sampler functions with one polymorphic
             * texture()
             * function we use the preprocessor to map the old names onto the
             * new
             * name...
             */
            strings[count] = "#define texture2D texture\n"
                             "#define texture3D texture\n"
                             "#define textureRect texture\n";
            lengths[count++] = -1;
        }

        strings[count] = vertex_boilerplate;
        lengths[count++] = strlen(vertex_boilerplate);
    } else if (shader_gl_type == GL_FRAGMENT_SHADER) {
        if (dev->glsl_version_to_use < 130) {
            strings[count] = "#define in varying\n"
                             "\n"
                             "#define cg_color_out gl_FragColor\n"
                             "#define cg_depth_out gl_FragDepth\n";
            lengths[count++] = -1;
        }

        strings[count] = fragment_boilerplate;
        lengths[count++] = strlen(fragment_boilerplate);

        if (dev->glsl_version_to_use >= 130) {
            /* FIXME: Support cg_depth_out. */
            /* To support source compatibility with glsl >= 1.3 which has
             * replaced
             * all of the texture sampler functions with one polymorphic
             * texture()
             * function we use the preprocessor to map the old names onto the
             * new
             * name...
             */
            strings[count] = "#define texture2D texture\n"
                             "#define texture3D texture\n"
                             "#define textureRect texture\n"
                             "\n"
                             "out vec4 cg_color_out;\n";
            //"out vec4 cg_depth_out\n";
            lengths[count++] = -1;
        }
    }

    memcpy(strings + count, strings_in, sizeof(char *) * count_in);
    if (lengths_in)
        memcpy(lengths + count, lengths_in, sizeof(GLint) * count_in);
    else {
        int i;

        for (i = 0; i < count_in; i++)
            lengths[count + i] = -1; /* null terminated */
    }
    count += count_in;

    if (C_UNLIKELY(CG_DEBUG_ENABLED(CG_DEBUG_SHOW_SOURCE))) {
        c_string_t *buf = c_string_new(NULL);
        int i;

        c_string_append_printf(buf,
                               "%s shader:\n",
                               shader_gl_type == GL_VERTEX_SHADER ? "vertex"
                               : "fragment");
        for (i = 0; i < count; i++)
            if (lengths[i] != -1)
                c_string_append_len(buf, strings[i], lengths[i]);
            else
                c_string_append(buf, strings[i]);

        c_message("%s", buf->str);

        c_string_free(buf, true);
    }

    GE(dev,
       glShaderSource(
           shader_gl_handle, count, (const char **)strings, lengths));

    c_free(version_string);
}
Beispiel #8
0
/*
 * _cg_pipeline_flush_gl_state:
 *
 * Details of override options:
 * ->fallback_mask: is a bitmask of the pipeline layers that need to be
 *    replaced with the default, fallback textures. The fallback textures are
 *    fully transparent textures so they hopefully wont contribute to the
 *    texture combining.
 *
 *    The intention of fallbacks is to try and preserve
 *    the number of layers the user is expecting so that texture coordinates
 *    they gave will mostly still correspond to the textures they intended, and
 *    have a fighting chance of looking close to their originally intended
 *    result.
 *
 * ->disable_mask: is a bitmask of the pipeline layers that will simply have
 *    texturing disabled. It's only really intended for disabling all layers
 *    > X; i.e. we'd expect to see a contiguous run of 0 starting from the LSB
 *    and at some point the remaining bits flip to 1. It might work to disable
 *    arbitrary layers; though I'm not sure a.t.m how OpenGL would take to
 *    that.
 *
 *    The intention of the disable_mask is for emitting geometry when the user
 *    hasn't supplied enough texture coordinates for all the layers and it's
 *    not possible to auto generate default texture coordinates for those
 *    layers.
 *
 * ->layer0_override_texture: forcibly tells us to bind this GL texture name for
 *    layer 0 instead of plucking the gl_texture from the cg_texture_t of layer
 *    0.
 *
 *    The intention of this is for any primitives that supports sliced textures.
 *    The code will can iterate each of the slices and re-flush the pipeline
 *    forcing the GL texture of each slice in turn.
 *
 * ->wrap_mode_overrides: overrides the wrap modes set on each
 *    layer. This is used to implement the automatic wrap mode.
 *
 * XXX: It might also help if we could specify a texture matrix for code
 *    dealing with slicing that would be multiplied with the users own matrix.
 *
 *    Normaly texture coords in the range [0, 1] refer to the extents of the
 *    texture, but when your GL texture represents a slice of the real texture
 *    (from the users POV) then a texture matrix would be a neat way of
 *    transforming the mapping for each slice.
 *
 *    Currently for textured rectangles we manually calculate the texture
 *    coords for each slice based on the users given coords, but this solution
 *    isn't ideal, and can't be used with CGlibVertexBuffers.
 */
void
_cg_pipeline_flush_gl_state(cg_device_t *dev,
                            cg_pipeline_t *pipeline,
                            cg_framebuffer_t *framebuffer,
                            bool with_color_attrib,
                            bool unknown_color_alpha)
{
    cg_pipeline_t *current_pipeline = dev->current_pipeline;
    unsigned long pipelines_difference;
    int n_layers;
    unsigned long *layer_differences;
    int i;
    cg_texture_unit_t *unit1;
    const cg_pipeline_progend_t *progend;

    CG_STATIC_TIMER(pipeline_flush_timer,
                    "Mainloop", /* parent */
                    "Material Flush",
                    "The time spent flushing material state",
                    0 /* no application private data */);

    CG_TIMER_START(_cg_uprof_context, pipeline_flush_timer);

#warning "HACK"
    current_pipeline = NULL;

    /* Bail out asap if we've been asked to re-flush the already current
     * pipeline and we can see the pipeline hasn't changed */
    if (current_pipeline == pipeline &&
        dev->current_pipeline_age == pipeline->age &&
        dev->current_pipeline_with_color_attrib == with_color_attrib &&
        dev->current_pipeline_unknown_color_alpha == unknown_color_alpha)
        goto done;
    else {
        /* Update derived state (currently just the 'real_blend_enable'
         * state) and determine a mask of state that differs between the
         * current pipeline and the one we are flushing.
         *
         * Note updating the derived state is done before doing any
         * pipeline comparisons so that we can correctly compare the
         * 'real_blend_enable' state itself.
         */

        if (current_pipeline == pipeline) {
            pipelines_difference = dev->current_pipeline_changes_since_flush;

            if (pipelines_difference & CG_PIPELINE_STATE_AFFECTS_BLENDING ||
                pipeline->unknown_color_alpha != unknown_color_alpha) {
                bool save_real_blend_enable = pipeline->real_blend_enable;

                _cg_pipeline_update_real_blend_enable(pipeline,
                                                      unknown_color_alpha);

                if (save_real_blend_enable != pipeline->real_blend_enable)
                    pipelines_difference |= CG_PIPELINE_STATE_REAL_BLEND_ENABLE;
            }
        } else if (current_pipeline) {
            pipelines_difference = dev->current_pipeline_changes_since_flush;

            _cg_pipeline_update_real_blend_enable(pipeline,
                                                  unknown_color_alpha);

            pipelines_difference |= _cg_pipeline_compare_differences(dev->current_pipeline,
                                                                     pipeline);
        } else {
            _cg_pipeline_update_real_blend_enable(pipeline,
                                                  unknown_color_alpha);

            pipelines_difference = CG_PIPELINE_STATE_ALL;
        }
    }

    /* Get a layer_differences mask for each layer to be flushed */
    n_layers = cg_pipeline_get_n_layers(pipeline);
    if (n_layers) {
        cg_pipeline_compare_layers_state_t state;

        layer_differences = c_alloca(sizeof(unsigned long) * n_layers);
        memset(layer_differences, 0, sizeof(unsigned long) * n_layers);

        state.dev = dev;
        state.i = 0;
        state.layer_differences = layer_differences;

        _cg_pipeline_foreach_layer_internal(pipeline,
                                            compare_layer_differences_cb,
                                            &state);
    } else
        layer_differences = NULL;

    /* First flush everything that's the same regardless of which
     * pipeline backend is being used...
     *
     * 1) top level state:
     *  glColor (or skip if a vertex attribute is being used for color)
     *  blend state
     *  alpha test state (except for GLES 2.0)
     *
     * 2) then foreach layer:
     *  determine gl_target/gl_texture
     *  bind texture
     *
     *  Note: After _cg_pipeline_flush_common_gl_state you can expect
     *  all state of the layers corresponding texture unit to be
     *  updated.
     */
    _cg_pipeline_flush_common_gl_state(dev,
                                       pipeline,
                                       pipelines_difference,
                                       layer_differences,
                                       with_color_attrib);

    /* Now flush the fragment, vertex and program state according to the
     * current progend backend.
     *
     * Note: Some backends may not support the current pipeline
     * configuration and in that case it will report and error and we
     * will look for a different backend.
     *
     * NB: if pipeline->progend != CG_PIPELINE_PROGEND_UNDEFINED then
     * we have previously managed to successfully flush this pipeline
     * with the given progend so we will simply use that to avoid
     * fallback code paths.
     */
    if (pipeline->progend == CG_PIPELINE_PROGEND_UNDEFINED)
        _cg_pipeline_set_progend(pipeline, CG_PIPELINE_PROGEND_DEFAULT);

    for (i = pipeline->progend; i < CG_PIPELINE_N_PROGENDS;
         i++, _cg_pipeline_set_progend(pipeline, i)) {
        const cg_pipeline_vertend_t *vertend;
        const cg_pipeline_fragend_t *fragend;
        cg_pipeline_add_layer_state_t state;

        progend = _cg_pipeline_progends[i];

        if (C_UNLIKELY(!progend->start(dev, pipeline)))
            continue;

        vertend = _cg_pipeline_vertends[progend->vertend];

        vertend->start(dev, pipeline, n_layers, pipelines_difference);

        state.dev = dev;
        state.framebuffer = framebuffer;
        state.vertend = vertend;
        state.pipeline = pipeline;
        state.layer_differences = layer_differences;
        state.error_adding_layer = false;
        state.added_layer = false;

        _cg_pipeline_foreach_layer_internal(
            pipeline, vertend_add_layer_cb, &state);

        if (C_UNLIKELY(state.error_adding_layer))
            continue;

        if (C_UNLIKELY(!vertend->end(dev, pipeline, pipelines_difference)))
            continue;

        /* Now prepare the fragment processing state (fragend)
         *
         * NB: We can't combine the setup of the vertend and fragend
         * since the backends that do code generation share
         * dev->codegen_source_buffer as a scratch buffer.
         */

        fragend = _cg_pipeline_fragends[progend->fragend];
        state.fragend = fragend;

        fragend->start(dev, pipeline, n_layers, pipelines_difference);

        _cg_pipeline_foreach_layer_internal(
            pipeline, fragend_add_layer_cb, &state);

        if (C_UNLIKELY(state.error_adding_layer))
            continue;

        if (C_UNLIKELY(!fragend->end(dev, pipeline, pipelines_difference)))
            continue;

        if (progend->end)
            progend->end(dev, pipeline, pipelines_difference);
        break;
    }

    /* Since the NOP progend will claim to handle anything we should
     * never fall through without finding a suitable progend */
    c_assert(i != CG_PIPELINE_N_PROGENDS);

    /* FIXME: This reference is actually resulting in lots of
     * copy-on-write reparenting because one-shot pipelines end up
     * living for longer than necessary and so any later modification of
     * the parent will cause a copy-on-write.
     *
     * XXX: The issue should largely go away when we switch to using
     * weak pipelines for overrides.
     */
    cg_object_ref(pipeline);
    if (dev->current_pipeline != NULL)
        cg_object_unref(dev->current_pipeline);
    dev->current_pipeline = pipeline;
    dev->current_pipeline_changes_since_flush = 0;
    dev->current_pipeline_with_color_attrib = with_color_attrib;
    dev->current_pipeline_unknown_color_alpha = unknown_color_alpha;
    dev->current_pipeline_age = pipeline->age;

done:

    progend = _cg_pipeline_progends[pipeline->progend];

    /* We can't assume the color will be retained between flushes when
     * using the glsl progend because the generic attribute values are
     * not stored as part of the program object so they could be
     * overridden by any attribute changes in another program */
    if (pipeline->progend == CG_PIPELINE_PROGEND_GLSL && !with_color_attrib) {
        int attribute;
        cg_pipeline_t *authority =
            _cg_pipeline_get_authority(pipeline, CG_PIPELINE_STATE_COLOR);
        int name_index = CG_ATTRIBUTE_COLOR_NAME_INDEX;

        attribute =
            _cg_pipeline_progend_glsl_get_attrib_location(dev, pipeline,
                                                          name_index);
        if (attribute != -1)
            GE(dev,
               glVertexAttrib4f(attribute,
                                authority->color.red,
                                authority->color.green,
                                authority->color.blue,
                                authority->color.alpha));
    }

    /* Give the progend a chance to update any uniforms that might not
     * depend on the material state. This is used on GLES2 to update the
     * matrices */
    if (progend->pre_paint)
        progend->pre_paint(dev, pipeline, framebuffer);

    /* Handle the fact that OpenGL associates texture filter and wrap
     * modes with the texture objects not the texture units... */
    if (!_cg_has_private_feature(dev, CG_PRIVATE_FEATURE_SAMPLER_OBJECTS))
        foreach_texture_unit_update_filter_and_wrap_modes(dev);

    /* If this pipeline has more than one layer then we always need
     * to make sure we rebind the texture for unit 1.
     *
     * NB: various components of CGlib may temporarily bind arbitrary
     * textures to texture unit 1 so they can query and modify texture
     * object parameters. cg-pipeline.c (See
     * _cg_bind_gl_texture_transient)
     */
    unit1 = _cg_get_texture_unit(dev, 1);
    if (cg_pipeline_get_n_layers(pipeline) > 1 && unit1->dirty_gl_texture) {
        set_active_texture_unit(dev, 1);
        GE(dev, glBindTexture(unit1->gl_target, unit1->gl_texture));
        unit1->dirty_gl_texture = false;
    }

    CG_TIMER_STOP(_cg_uprof_context, pipeline_flush_timer);
}