void cogl_begin_gl (void) { CoglPipeline *pipeline; _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (ctx->in_begin_gl_block) { static gboolean shown = FALSE; if (!shown) g_warning ("You should not nest cogl_begin_gl/cogl_end_gl blocks"); shown = TRUE; return; } ctx->in_begin_gl_block = TRUE; /* Flush all batched primitives */ cogl_flush (); /* Flush framebuffer state, including clip state, modelview and * projection matrix state * * NB: _cogl_framebuffer_flush_state may disrupt various state (such * as the pipeline state) when flushing the clip stack, so should * always be done first when preparing to draw. */ _cogl_framebuffer_flush_state (cogl_get_draw_framebuffer (), _cogl_get_read_framebuffer (), COGL_FRAMEBUFFER_STATE_ALL); /* Setup the state for the current pipeline */ /* We considered flushing a specific, minimal pipeline here to try and * simplify the GL state, but decided to avoid special cases and second * guessing what would be actually helpful. * * A user should instead call cogl_set_source_color4ub() before * cogl_begin_gl() to simplify the state flushed. * * XXX: note defining n_tex_coord_attribs using * cogl_pipeline_get_n_layers is a hack, but the problem is that * n_tex_coord_attribs is usually defined when drawing a primitive * which isn't happening here. * * Maybe it would be more useful if this code did flush the * opaque_color_pipeline and then call into cogl-pipeline-opengl.c to then * restore all state for the material's backend back to default OpenGL * values. */ pipeline = cogl_get_source (); _cogl_pipeline_flush_gl_state (pipeline, FALSE, cogl_pipeline_get_n_layers (pipeline)); /* Disable any cached vertex arrays */ _cogl_attribute_disable_cached_arrays (); }
void cogl_path_stroke (CoglPath *path, CoglFramebuffer *framebuffer, CoglPipeline *pipeline) { CoglPathData *data; CoglPipeline *copy = NULL; unsigned int path_start; int path_num = 0; CoglPathNode *node; _COGL_RETURN_IF_FAIL (cogl_is_path (path)); _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (framebuffer)); _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); data = path->data; if (data->path_nodes->len == 0) return; if (cogl_pipeline_get_n_layers (pipeline) != 0) { copy = cogl_pipeline_copy (pipeline); _cogl_pipeline_prune_to_n_layers (copy, 0); pipeline = copy; } _cogl_path_build_stroke_attribute_buffer (path); for (path_start = 0; path_start < data->path_nodes->len; path_start += node->path_size) { CoglPrimitive *primitive; node = &g_array_index (data->path_nodes, CoglPathNode, path_start); primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_LINE_STRIP, node->path_size, &data->stroke_attributes[path_num], 1); cogl_primitive_draw (primitive, framebuffer, pipeline); cogl_object_unref (primitive); path_num++; } if (copy) cogl_object_unref (copy); }
static void _cogl_path_stroke_nodes (CoglPath *path) { CoglPathData *data = path->data; CoglPipeline *copy = NULL; CoglPipeline *source; unsigned int path_start; int path_num = 0; CoglPathNode *node; _COGL_GET_CONTEXT (ctx, NO_RETVAL); source = cogl_get_source (); if (cogl_pipeline_get_n_layers (source) != 0) { copy = cogl_pipeline_copy (source); _cogl_pipeline_prune_to_n_layers (copy, 0); source = copy; } _cogl_path_build_stroke_attribute_buffer (path); cogl_push_source (source); for (path_start = 0; path_start < data->path_nodes->len; path_start += node->path_size) { node = &g_array_index (data->path_nodes, CoglPathNode, path_start); cogl_framebuffer_vdraw_attributes (cogl_get_draw_framebuffer (), source, COGL_VERTICES_MODE_LINE_STRIP, 0, node->path_size, data->stroke_attributes[path_num], NULL); path_num++; } cogl_pop_source (); if (copy) cogl_object_unref (copy); }
/* This path supports multitexturing but only when each of the layers is * handled with a single GL texture. Also if repeating is necessary then * _cogl_texture_can_hardware_repeat() must return TRUE. * This includes layers made from: * * - CoglTexture2DSliced: if only comprised of a single slice with optional * waste, assuming the users given texture coordinates don't require * repeating. * - CoglTexture{1D,2D,3D}: always. * - CoglTexture2DAtlas: assuming the users given texture coordinates don't * require repeating. * - CoglTextureRectangle: assuming the users given texture coordinates don't * require repeating. * - CoglTexturePixmap: assuming the users given texture coordinates don't * require repeating. */ static CoglBool _cogl_multitexture_quad_single_primitive (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, const float *position, const float *user_tex_coords, int user_tex_coords_len) { int n_layers = cogl_pipeline_get_n_layers (pipeline); ValidateTexCoordsState state; float *final_tex_coords = alloca (sizeof (float) * 4 * n_layers); state.i = -1; state.n_layers = n_layers; state.user_tex_coords = user_tex_coords; state.user_tex_coords_len = user_tex_coords_len; state.final_tex_coords = final_tex_coords; state.override_pipeline = NULL; state.needs_multiple_primitives = FALSE; cogl_pipeline_foreach_layer (pipeline, validate_tex_coords_cb, &state); if (state.needs_multiple_primitives) return FALSE; if (state.override_pipeline) pipeline = state.override_pipeline; _cogl_journal_log_quad (framebuffer->journal, position, pipeline, n_layers, NULL, /* no texture override */ final_tex_coords, n_layers * 4); if (state.override_pipeline) cogl_object_unref (state.override_pipeline); return TRUE; }
void _cogl_glsl_shader_set_source_with_boilerplate (CoglContext *ctx, GLuint shader_gl_handle, GLenum shader_gl_type, CoglPipeline *pipeline, GLsizei count_in, const char **strings_in, const GLint *lengths_in) { const char *vertex_boilerplate; const char *fragment_boilerplate; const char **strings = g_alloca (sizeof (char *) * (count_in + 4)); GLint *lengths = g_alloca (sizeof (GLint) * (count_in + 4)); char *version_string; int count = 0; int n_layers; vertex_boilerplate = _COGL_VERTEX_SHADER_BOILERPLATE; fragment_boilerplate = _COGL_FRAGMENT_SHADER_BOILERPLATE; version_string = g_strdup_printf ("#version %i\n\n", ctx->glsl_version_to_use); strings[count] = version_string; lengths[count++] = -1; if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_GL_EMBEDDED) && cogl_has_feature (ctx, COGL_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 (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_EGL_IMAGE_EXTERNAL)) { static const char texture_3d_extension[] = "#extension GL_OES_EGL_image_external : require\n"; strings[count] = texture_3d_extension; lengths[count++] = sizeof (texture_3d_extension) - 1; } if (shader_gl_type == GL_VERTEX_SHADER) { strings[count] = vertex_boilerplate; lengths[count++] = strlen (vertex_boilerplate); } else if (shader_gl_type == GL_FRAGMENT_SHADER) { strings[count] = fragment_boilerplate; lengths[count++] = strlen (fragment_boilerplate); } n_layers = cogl_pipeline_get_n_layers (pipeline); if (n_layers) { GString *layer_declarations = ctx->codegen_boilerplate_buffer; g_string_set_size (layer_declarations, 0); g_string_append_printf (layer_declarations, "varying vec4 _cogl_tex_coord[%d];\n", n_layers); if (shader_gl_type == GL_VERTEX_SHADER) { g_string_append_printf (layer_declarations, "uniform mat4 cogl_texture_matrix[%d];\n", n_layers); _cogl_pipeline_foreach_layer_internal (pipeline, add_layer_vertex_boilerplate_cb, layer_declarations); } else if (shader_gl_type == GL_FRAGMENT_SHADER) { _cogl_pipeline_foreach_layer_internal (pipeline, add_layer_fragment_boilerplate_cb, layer_declarations); } strings[count] = layer_declarations->str; lengths[count++] = -1; /* null terminated */ } 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 (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_SHOW_SOURCE))) { GString *buf = g_string_new (NULL); int i; g_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) g_string_append_len (buf, strings[i], lengths[i]); else g_string_append (buf, strings[i]); g_message ("%s", buf->str); g_string_free (buf, TRUE); } GE( ctx, glShaderSource (shader_gl_handle, count, (const char **) strings, lengths) ); g_free (version_string); }
static gboolean _cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline, unsigned long pipelines_difference) { CoglPipelineShaderState *shader_state; _COGL_GET_CONTEXT (ctx, FALSE); shader_state = get_shader_state (pipeline); if (shader_state->source) { const char *source_strings[2]; GLint lengths[2]; GLint compile_status; GLuint shader; int n_layers; COGL_STATIC_COUNTER (vertend_glsl_compile_counter, "glsl vertex compile counter", "Increments each time a new GLSL " "vertex shader is compiled", 0 /* no application private data */); COGL_COUNTER_INC (_cogl_uprof_context, vertend_glsl_compile_counter); g_string_append (shader_state->source, " cogl_position_out = " "cogl_modelview_projection_matrix * " "cogl_position_in;\n" " cogl_color_out = cogl_color_in;\n" "}\n"); GE_RET( shader, ctx, glCreateShader (GL_VERTEX_SHADER) ); lengths[0] = shader_state->header->len; source_strings[0] = shader_state->header->str; lengths[1] = shader_state->source->len; source_strings[1] = shader_state->source->str; n_layers = cogl_pipeline_get_n_layers (pipeline); _cogl_shader_set_source_with_boilerplate (shader, GL_VERTEX_SHADER, n_layers, 2, /* count */ source_strings, lengths); GE( ctx, glCompileShader (shader) ); GE( ctx, glGetShaderiv (shader, GL_COMPILE_STATUS, &compile_status) ); if (!compile_status) { GLint len = 0; char *shader_log; GE( ctx, glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &len) ); shader_log = g_alloca (len); GE( ctx, glGetShaderInfoLog (shader, len, &len, shader_log) ); g_warning ("Shader compilation failed:\n%s", shader_log); } shader_state->header = NULL; shader_state->source = NULL; shader_state->gl_shader = shader; } return TRUE; }
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 CoglBool _cogl_rectangles_validate_layer_cb (CoglPipeline *pipeline, int layer_index, void *user_data) { ValidateLayerState *state = user_data; CoglTexture *texture; state->i++; /* We need to ensure the mipmaps are ready before deciding * anything else about the texture because the texture storage * could completely change if it needs to be migrated out of the * atlas and will affect how we validate the layer. * * FIXME: this needs to be generalized. There could be any * number of things that might require a shuffling of the * underlying texture storage. We could add two mechanisms to * generalize this a bit... * * 1) add a _cogl_pipeline_layer_update_storage() function that * would for instance consider if mipmapping is necessary and * potentially migrate the texture from an atlas. * * 2) allow setting of transient primitive-flags on a pipeline * that may affect the outcome of _update_storage(). One flag * could indicate that we expect to sample beyond the bounds of * the texture border. * * flags = COGL_PIPELINE_PRIMITIVE_FLAG_VALID_BORDERS; * _cogl_pipeline_layer_assert_primitive_flags (layer, flags) * _cogl_pipeline_layer_update_storage (layer) * enqueue primitive in journal * * when the primitive is dequeued and drawn we should: * _cogl_pipeline_flush_gl_state (pipeline) * draw primitive * _cogl_pipeline_unassert_primitive_flags (layer, flags); * * _cogl_pipeline_layer_update_storage should take into * consideration all the asserted primitive requirements. (E.g. * there could be multiple primitives in the journal - or in a * renderlist in the future - that need mipmaps or that need * valid contents beyond their borders (for cogl_polygon) * meaning they can't work with textures in an atas, so * _cogl_pipeline_layer_update_storage would pass on these * requirements to the texture atlas backend which would make * sure the referenced texture is migrated out of the atlas and * mipmaps are generated.) */ _cogl_pipeline_pre_paint_for_layer (pipeline, layer_index); texture = cogl_pipeline_get_layer_texture (pipeline, layer_index); /* NULL textures are handled by * _cogl_pipeline_flush_gl_state */ if (texture == NULL) return TRUE; if (state->i == 0) state->first_layer = layer_index; /* XXX: * For now, if the first layer is sliced then all other layers are * ignored since we currently don't support multi-texturing with * sliced textures. If the first layer is not sliced then any other * layers found to be sliced will be skipped. (with a warning) * * TODO: Add support for multi-texturing rectangles with sliced * textures if no texture matrices are in use. */ if (cogl_texture_is_sliced (texture)) { if (state->i == 0) { if (cogl_pipeline_get_n_layers (pipeline) > 1) { static CoglBool warning_seen = FALSE; if (!state->override_source) state->override_source = cogl_pipeline_copy (pipeline); _cogl_pipeline_prune_to_n_layers (state->override_source, 1); if (!warning_seen) g_warning ("Skipping layers 1..n of your pipeline since " "the first layer is sliced. We don't currently " "support any multi-texturing with sliced " "textures but assume layer 0 is the most " "important to keep"); warning_seen = TRUE; } state->all_use_sliced_quad_fallback = TRUE; return FALSE; } else { static CoglBool warning_seen = FALSE; CoglTexture2D *tex_2d; if (!warning_seen) g_warning ("Skipping layer %d of your pipeline consisting of " "a sliced texture (unsuported for multi texturing)", state->i); warning_seen = TRUE; /* Note: currently only 2D textures can be sliced. */ tex_2d = state->ctx->default_gl_texture_2d_tex; cogl_pipeline_set_layer_texture (pipeline, layer_index, COGL_TEXTURE (tex_2d)); return TRUE; } } #ifdef COGL_ENABLE_DEBUG /* If the texture can't be repeated with the GPU (e.g. because it has * waste or if using GL_TEXTURE_RECTANGLE_ARB) then if a texture matrix * is also in use we don't know if the result will end up trying * to texture from the waste area. * * Note: we check can_hardware_repeat() first since it's cheaper. * * Note: cases where the texture coordinates will require repeating * will be caught by later validation. */ if (!_cogl_texture_can_hardware_repeat (texture) && _cogl_pipeline_layer_has_user_matrix (pipeline, layer_index)) { static CoglBool warning_seen = FALSE; if (!warning_seen) g_warning ("layer %d of your pipeline uses a custom " "texture matrix but because the texture doesn't " "support hardware repeating you may see artefacts " "due to sampling beyond the texture's bounds.", state->i); warning_seen = TRUE; } #endif return TRUE; }