diff --git a/cogl/cogl-attribute-private.h b/cogl/cogl-attribute-private.h index a27924b02..d3abcdcee 100644 --- a/cogl/cogl-attribute-private.h +++ b/cogl/cogl-attribute-private.h @@ -40,6 +40,7 @@ typedef enum COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY, COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY, COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY, + COGL_ATTRIBUTE_NAME_ID_POINT_SIZE_ARRAY, COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY } CoglAttributeNameID; diff --git a/cogl/cogl-attribute.c b/cogl/cogl-attribute.c index bc45399ff..49920071e 100644 --- a/cogl/cogl-attribute.c +++ b/cogl/cogl-attribute.c @@ -101,6 +101,8 @@ validate_cogl_attribute_name (const char *name, *name_id = COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY; *normalized = TRUE; } + else if (strcmp (name, "point_size_in") == 0) + *name_id = COGL_ATTRIBUTE_NAME_ID_POINT_SIZE_ARRAY; else { g_warning ("Unknown cogl_* attribute name cogl_%s\n", name); @@ -193,6 +195,14 @@ validate_n_components (const CoglAttributeNameState *name_state, return FALSE; } break; + case COGL_ATTRIBUTE_NAME_ID_POINT_SIZE_ARRAY: + if (G_UNLIKELY (n_components != 1)) + { + g_critical ("The point size attribute can only have one " + "component"); + return FALSE; + } + break; case COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY: return TRUE; } diff --git a/cogl/cogl-attribute.h b/cogl/cogl-attribute.h index 580446e87..d350833aa 100644 --- a/cogl/cogl-attribute.h +++ b/cogl/cogl-attribute.h @@ -76,6 +76,11 @@ COGL_BEGIN_DECLS * "cogl_tex_coord0_in", "cogl_tex_coord1", ... * (used for vertex texture coordinates) * "cogl_normal_in" (used for vertex normals) + * "cogl_point_size_in" (used to set the size of points + * per-vertex. Note this can only be used if + * %COGL_FEATURE_ID_POINT_SIZE_ATTRIBUTE is advertised and + * cogl_pipeline_set_per_vertex_point_size() is called on the pipeline. + * * * * The attribute values corresponding to different vertices can either diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index 96254b926..7365f9172 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -95,6 +95,9 @@ _cogl_init_feature_overrides (CoglContext *ctx) { ctx->feature_flags &= ~COGL_FEATURE_SHADERS_GLSL; COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_GLSL, FALSE); + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE, + FALSE); } if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_NPOT_TEXTURES))) diff --git a/cogl/cogl-context.h b/cogl/cogl-context.h index 0dfb5e576..e574bc27a 100644 --- a/cogl/cogl-context.h +++ b/cogl/cogl-context.h @@ -214,6 +214,8 @@ cogl_is_context (void *object); * @COGL_FEATURE_ID_DEPTH_RANGE: cogl_pipeline_set_depth_range() support * @COGL_FEATURE_ID_POINT_SPRITE: Whether * cogl_pipeline_set_layer_point_sprite_coords_enabled() is supported. + * @COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE: Whether cogl_point_size_in + * can be used as an attribute to set a per-vertex point size. * @COGL_FEATURE_ID_MAP_BUFFER_FOR_READ: Whether cogl_buffer_map() is * supported with CoglBufferAccess including read support. * @COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE: Whether cogl_buffer_map() is @@ -260,6 +262,7 @@ typedef enum _CoglFeatureID COGL_FEATURE_ID_DEPTH_TEXTURE, COGL_FEATURE_ID_PRESENTATION_TIME, COGL_FEATURE_ID_FENCE, + COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE, /*< private >*/ _COGL_N_FEATURE_IDS /*< skip >*/ diff --git a/cogl/cogl-glsl-shader-boilerplate.h b/cogl/cogl-glsl-shader-boilerplate.h index 7a79fd335..78762d45c 100644 --- a/cogl/cogl-glsl-shader-boilerplate.h +++ b/cogl/cogl-glsl-shader-boilerplate.h @@ -32,8 +32,7 @@ "\n" \ "uniform mat4 cogl_modelview_matrix;\n" \ "uniform mat4 cogl_modelview_projection_matrix;\n" \ - "uniform mat4 cogl_projection_matrix;\n" \ - "uniform float cogl_point_size_in;\n" + "uniform mat4 cogl_projection_matrix;\n" /* This declares all of the variables that we might need. This is * working on the assumption that the compiler will optimise them out diff --git a/cogl/cogl-pipeline-private.h b/cogl/cogl-pipeline-private.h index 3e385f537..3d8972143 100644 --- a/cogl/cogl-pipeline-private.h +++ b/cogl/cogl-pipeline-private.h @@ -118,6 +118,7 @@ typedef enum COGL_PIPELINE_STATE_DEPTH_INDEX, COGL_PIPELINE_STATE_FOG_INDEX, COGL_PIPELINE_STATE_POINT_SIZE_INDEX, + COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE_INDEX, COGL_PIPELINE_STATE_LOGIC_OPS_INDEX, COGL_PIPELINE_STATE_CULL_FACE_INDEX, COGL_PIPELINE_STATE_UNIFORMS_INDEX, @@ -167,6 +168,8 @@ typedef enum _CoglPipelineState 1L<big_state->point_size == authority1->big_state->point_size; } +CoglBool +_cogl_pipeline_per_vertex_point_size_equal (CoglPipeline *authority0, + CoglPipeline *authority1) +{ + return (authority0->big_state->per_vertex_point_size == + authority1->big_state->per_vertex_point_size); +} + CoglBool _cogl_pipeline_logic_ops_state_equal (CoglPipeline *authority0, CoglPipeline *authority1) @@ -1398,6 +1406,63 @@ cogl_pipeline_set_point_size (CoglPipeline *pipeline, _cogl_pipeline_point_size_equal); } +CoglBool +cogl_pipeline_set_per_vertex_point_size (CoglPipeline *pipeline, + CoglBool enable, + CoglError **error) +{ + CoglPipelineState state = COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE; + CoglPipeline *authority; + + _COGL_GET_CONTEXT (ctx, FALSE); + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + + authority = _cogl_pipeline_get_authority (pipeline, state); + + enable = !!enable; + + if (authority->big_state->per_vertex_point_size == enable) + return TRUE; + + if (enable && !cogl_has_feature (ctx, COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE)) + { + _cogl_set_error (error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_UNSUPPORTED, + "Per-vertex point size is not supported"); + + return FALSE; + } + + /* - Flush journal primitives referencing the current state. + * - Make sure the pipeline has no dependants so it may be modified. + * - If the pipeline isn't currently an authority for the state being + * changed, then initialize that state from the current authority. + */ + _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); + + pipeline->big_state->per_vertex_point_size = enable; + + _cogl_pipeline_update_authority (pipeline, authority, state, + _cogl_pipeline_point_size_equal); + + return TRUE; +} + +CoglBool +cogl_pipeline_get_per_vertex_point_size (CoglPipeline *pipeline) +{ + CoglPipeline *authority; + + _COGL_RETURN_VAL_IF_FAIL (cogl_is_pipeline (pipeline), FALSE); + + authority = + _cogl_pipeline_get_authority (pipeline, + COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE); + + return authority->big_state->per_vertex_point_size; +} + static CoglBoxedValue * _cogl_pipeline_override_uniform (CoglPipeline *pipeline, int location) @@ -1832,6 +1897,16 @@ _cogl_pipeline_hash_point_size_state (CoglPipeline *authority, sizeof (point_size)); } +void +_cogl_pipeline_hash_per_vertex_point_size_state (CoglPipeline *authority, + CoglPipelineHashState *state) +{ + CoglBool per_vertex_point_size = authority->big_state->per_vertex_point_size; + state->hash = _cogl_util_one_at_a_time_hash (state->hash, + &per_vertex_point_size, + sizeof (per_vertex_point_size)); +} + void _cogl_pipeline_hash_logic_ops_state (CoglPipeline *authority, CoglPipelineHashState *state) diff --git a/cogl/cogl-pipeline-state.h b/cogl/cogl-pipeline-state.h index 44c50272b..1c68db79d 100644 --- a/cogl/cogl-pipeline-state.h +++ b/cogl/cogl-pipeline-state.h @@ -516,6 +516,50 @@ cogl_pipeline_set_point_size (CoglPipeline *pipeline, float cogl_pipeline_get_point_size (CoglPipeline *pipeline); +/** + * cogl_pipeline_set_per_vertex_point_size: + * @pipeline: a #CoglPipeline pointer + * @enable: whether to enable per-vertex point size + * @error: a location to store a #CoglError if the change failed + * + * Sets whether to use a per-vertex point size or to use the value set + * by cogl_pipeline_set_point_size(). If per-vertex point size is + * enabled then the point size can be set for an individual point + * either by drawing with a #CoglAttribute with the name + * ‘cogl_point_size_in’ or by writing to the GLSL builtin + * ‘cogl_point_size_out’ from a vertex shader snippet. + * + * If per-vertex point size is enabled and this attribute is not used + * and cogl_point_size_out is not written to then the results are + * undefined. + * + * Note that enabling this will only work if the + * %COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE feature is available. If + * this is not available then the function will return %FALSE and set + * a #CoglError. + * + * Since: 2.0 + * Stability: Unstable + * Return value: %TRUE if the change suceeded or %FALSE otherwise + */ +CoglBool +cogl_pipeline_set_per_vertex_point_size (CoglPipeline *pipeline, + CoglBool enable, + CoglError **error); + +/** + * cogl_pipeline_get_per_vertex_point_size: + * @pipeline: a #CoglPipeline pointer + * + * Since: 2.0 + * Stability: Unstable + * Return value: %TRUE if the pipeline has per-vertex point size + * enabled or %FALSE otherwise. The per-vertex point size can be + * enabled with cogl_pipeline_set_per_vertex_point_size(). + */ +CoglBool +cogl_pipeline_get_per_vertex_point_size (CoglPipeline *pipeline); + /** * cogl_pipeline_get_color_mask: * @pipeline: a #CoglPipeline object. diff --git a/cogl/cogl-pipeline.c b/cogl/cogl-pipeline.c index 10599668c..dcf459aef 100644 --- a/cogl/cogl-pipeline.c +++ b/cogl/cogl-pipeline.c @@ -1051,6 +1051,9 @@ _cogl_pipeline_copy_differences (CoglPipeline *dest, if (differences & COGL_PIPELINE_STATE_POINT_SIZE) big_state->point_size = src->big_state->point_size; + if (differences & COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE) + big_state->per_vertex_point_size = src->big_state->per_vertex_point_size; + if (differences & COGL_PIPELINE_STATE_LOGIC_OPS) { memcpy (&big_state->logic_ops_state, @@ -1134,6 +1137,7 @@ _cogl_pipeline_init_multi_property_sparse_state (CoglPipeline *pipeline, case COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE: case COGL_PIPELINE_STATE_POINT_SIZE: case COGL_PIPELINE_STATE_USER_SHADER: + case COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE: case COGL_PIPELINE_STATE_REAL_BLEND_ENABLE: g_return_if_reached (); @@ -2319,6 +2323,11 @@ _cogl_pipeline_equal (CoglPipeline *pipeline0, authorities1[bit])) goto done; break; + case COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE_INDEX: + if (!_cogl_pipeline_per_vertex_point_size_equal (authorities0[bit], + authorities1[bit])) + goto done; + break; case COGL_PIPELINE_STATE_LOGIC_OPS_INDEX: if (!_cogl_pipeline_logic_ops_state_equal (authorities0[bit], authorities1[bit])) @@ -2768,6 +2777,8 @@ _cogl_pipeline_init_state_hash_functions (void) _cogl_pipeline_hash_cull_face_state; state_hash_functions[COGL_PIPELINE_STATE_POINT_SIZE_INDEX] = _cogl_pipeline_hash_point_size_state; + state_hash_functions[COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE_INDEX] = + _cogl_pipeline_hash_per_vertex_point_size_state; state_hash_functions[COGL_PIPELINE_STATE_LOGIC_OPS_INDEX] = _cogl_pipeline_hash_logic_ops_state; state_hash_functions[COGL_PIPELINE_STATE_UNIFORMS_INDEX] = @@ -2779,7 +2790,7 @@ _cogl_pipeline_init_state_hash_functions (void) { /* So we get a big error if we forget to update this code! */ - _COGL_STATIC_ASSERT (COGL_PIPELINE_STATE_SPARSE_COUNT == 16, + _COGL_STATIC_ASSERT (COGL_PIPELINE_STATE_SPARSE_COUNT == 17, "Make sure to install a hash function for " "newly added pipeline state and update assert " "in _cogl_pipeline_init_state_hash_functions"); diff --git a/cogl/cogl-private.h b/cogl/cogl-private.h index c17e86531..eb3a3dddd 100644 --- a/cogl/cogl-private.h +++ b/cogl/cogl-private.h @@ -61,7 +61,8 @@ typedef enum /* If this is set then the winsys is responsible for queueing dirty * events. Otherwise a dirty event will be queued when the onscreen * is first allocated or when it is shown or resized */ - COGL_PRIVATE_FEATURE_DIRTY_EVENTS = 1L<<25 + COGL_PRIVATE_FEATURE_DIRTY_EVENTS = 1L<<25, + COGL_PRIVATE_FEATURE_ENABLE_PROGRAM_POINT_SIZE = 1L<<26 } CoglPrivateFeatureFlags; /* Sometimes when evaluating pipelines, either during comparisons or diff --git a/cogl/cogl-snippet.h b/cogl/cogl-snippet.h index 2af31d4ac..e1b30ef8b 100644 --- a/cogl/cogl-snippet.h +++ b/cogl/cogl-snippet.h @@ -203,6 +203,16 @@ COGL_BEGIN_DECLS * * * float + * cogl_point_size_in + * + * The incoming point size from the cogl_point_size_in attribute. + * This is only available if + * cogl_pipeline_set_per_vertex_point_size() is set on the + * pipeline. + * + * + * + * float * cogl_point_size_out * * The calculated size of a point. This is equivalent to #gl_PointSize. @@ -321,6 +331,10 @@ typedef struct _CoglSnippet CoglSnippet; * @COGL_SNIPPET_HOOK_VERTEX: A hook for the entire vertex processing * stage of the pipeline. * @COGL_SNIPPET_HOOK_VERTEX_TRANSFORM: A hook for the vertex transformation. + * @COGL_SNIPPET_HOOK_POINT_SIZE: A hook for manipulating the point + * size of a vertex. This is only used if + * cogl_pipeline_set_per_vertex_point_size() is enabled on the + * pipeline. * @COGL_SNIPPET_HOOK_FRAGMENT: A hook for the entire fragment * processing stage of the pipeline. * @COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM: A hook for applying the @@ -422,6 +436,39 @@ typedef struct _CoglSnippet CoglSnippet; * * * + * %COGL_SNIPPET_HOOK_POINT_SIZE + * + * + * Adds a shader snippet that will hook on to the point size + * calculation step within the vertex shader stage. The snippet should + * write to the builtin cogl_point_size_out with the new point size. + * The snippet can either read cogl_point_size_in directly and write a + * new value or first read an existing value in cogl_point_size_out + * that would be set by a previous snippet. Note that this hook is + * only used if cogl_pipeline_set_per_vertex_point_size() is enabled + * on the pipeline. + * + * + * The ‘declarations’ string in @snippet will be inserted in the + * global scope of the shader. Use this to declare any uniforms, + * attributes or functions that the snippet requires. + * + * + * The ‘pre’ string in @snippet will be inserted just before + * calculating the point size. + * + * + * The ‘replace’ string in @snippet will be used instead of the + * generated point size calculation if it is present. + * + * + * The ‘post’ string in @snippet will be inserted after the + * standard point size calculation is done. This can be used to modify + * cogl_point_size_out in addition to the default processing. + * + * + * + * * %COGL_SNIPPET_HOOK_FRAGMENT * * @@ -583,6 +630,7 @@ typedef enum { COGL_SNIPPET_HOOK_VERTEX = 0, COGL_SNIPPET_HOOK_VERTEX_TRANSFORM, COGL_SNIPPET_HOOK_VERTEX_GLOBALS, + COGL_SNIPPET_HOOK_POINT_SIZE, /* Per pipeline fragment hooks */ COGL_SNIPPET_HOOK_FRAGMENT = 2048, diff --git a/cogl/driver/gl/cogl-pipeline-opengl.c b/cogl/driver/gl/cogl-pipeline-opengl.c index 735a45ccd..119b44905 100644 --- a/cogl/driver/gl/cogl-pipeline-opengl.c +++ b/cogl/driver/gl/cogl-pipeline-opengl.c @@ -58,7 +58,9 @@ #ifndef GL_CLAMP_TO_BORDER #define GL_CLAMP_TO_BORDER 0x812d #endif - +#ifndef GL_PROGRAM_POINT_SIZE +#define GL_PROGRAM_POINT_SIZE 0x8642 +#endif static void texture_unit_init (CoglContext *ctx, @@ -684,6 +686,21 @@ _cogl_pipeline_flush_color_blend_alpha_depth_state ( } } +#ifdef HAVE_COGL_GL + if ((ctx->private_feature_flags & + COGL_PRIVATE_FEATURE_ENABLE_PROGRAM_POINT_SIZE) && + (pipelines_difference & COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE)) + { + unsigned long state = COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE; + CoglPipeline *authority = _cogl_pipeline_get_authority (pipeline, state); + + if (authority->big_state->per_vertex_point_size) + GE( ctx, glEnable (GL_PROGRAM_POINT_SIZE) ); + else + GE( ctx, glDisable (GL_PROGRAM_POINT_SIZE) ); + } +#endif + if (pipeline->real_blend_enable != ctx->gl_blend_enable_cache) { if (pipeline->real_blend_enable) diff --git a/cogl/driver/gl/cogl-pipeline-progend-fixed.c b/cogl/driver/gl/cogl-pipeline-progend-fixed.c index 78a8c8d3b..558b5b803 100644 --- a/cogl/driver/gl/cogl-pipeline-progend-fixed.c +++ b/cogl/driver/gl/cogl-pipeline-progend-fixed.c @@ -64,6 +64,11 @@ _cogl_pipeline_progend_fixed_start (CoglPipeline *pipeline) if (cogl_pipeline_get_user_program (pipeline)) return FALSE; + /* The fixed progend can't handle the per-vertex point size + * attribute */ + if (cogl_pipeline_get_per_vertex_point_size (pipeline)) + return FALSE; + return TRUE; } diff --git a/cogl/driver/gl/cogl-pipeline-vertend-glsl.c b/cogl/driver/gl/cogl-pipeline-vertend-glsl.c index 742346d26..069573a18 100644 --- a/cogl/driver/gl/cogl-pipeline-vertend-glsl.c +++ b/cogl/driver/gl/cogl-pipeline-vertend-glsl.c @@ -290,12 +290,20 @@ _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline, "cogl_generated_source ()\n" "{\n"); - if (!(ctx->private_feature_flags & - COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM)) - /* There is no builtin uniform for the pointsize on GLES2 so we need - to copy it from the custom uniform in the vertex shader */ - g_string_append (shader_state->source, - " cogl_point_size_out = cogl_point_size_in;\n"); + if (cogl_pipeline_get_per_vertex_point_size (pipeline)) + g_string_append (shader_state->header, + "attribute float cogl_point_size_in;\n"); + else if (!(ctx->private_feature_flags & + COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM)) + { + /* There is no builtin uniform for the point size on GLES2 so we + need to copy it from the custom uniform in the vertex shader if + we're not using per-vertex point sizes */ + g_string_append (shader_state->header, + "uniform float cogl_point_size_in;\n"); + g_string_append (shader_state->source, + " cogl_point_size_out = cogl_point_size_in;\n"); + } } static CoglBool @@ -389,6 +397,8 @@ _cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline, GLuint shader; CoglPipelineSnippetData snippet_data; CoglPipelineSnippetList *vertex_snippets; + CoglBool has_per_vertex_point_size = + cogl_pipeline_get_per_vertex_point_size (pipeline); COGL_STATIC_COUNTER (vertend_glsl_compile_counter, "glsl vertex compile counter", @@ -407,7 +417,21 @@ _cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline, "}\n"); g_string_append (shader_state->source, - " cogl_vertex_transform ();\n" + " cogl_vertex_transform ();\n"); + + if (has_per_vertex_point_size) + { + g_string_append (shader_state->header, + "void\n" + "cogl_real_point_size_calculation ()\n" + "{\n" + " cogl_point_size_out = cogl_point_size_in;\n" + "}\n"); + g_string_append (shader_state->source, + " cogl_point_size_calculation ();\n"); + } + + g_string_append (shader_state->source, " cogl_color_out = cogl_color_in;\n" "}\n"); @@ -423,6 +447,19 @@ _cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline, snippet_data.source_buf = shader_state->header; _cogl_pipeline_snippet_generate_code (&snippet_data); + /* Add hooks for the point size calculation part */ + if (has_per_vertex_point_size) + { + memset (&snippet_data, 0, sizeof (snippet_data)); + snippet_data.snippets = vertex_snippets; + snippet_data.hook = COGL_SNIPPET_HOOK_POINT_SIZE; + snippet_data.chain_function = "cogl_real_point_size_calculation"; + snippet_data.final_name = "cogl_point_size_calculation"; + snippet_data.function_prefix = "cogl_point_size_calculation"; + snippet_data.source_buf = shader_state->header; + _cogl_pipeline_snippet_generate_code (&snippet_data); + } + /* Add all of the hooks for vertex processing */ memset (&snippet_data, 0, sizeof (snippet_data)); snippet_data.snippets = vertex_snippets; @@ -487,6 +524,7 @@ _cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline, shader_state->gl_shader = shader; } +#ifdef HAVE_COGL_GL if ((ctx->private_feature_flags & COGL_PRIVATE_FEATURE_BUILTIN_POINT_SIZE_UNIFORM) && (pipelines_difference & COGL_PIPELINE_STATE_POINT_SIZE)) @@ -496,6 +534,7 @@ _cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline, GE( ctx, glPointSize (authority->big_state->point_size) ); } +#endif /* HAVE_COGL_GL */ return TRUE; } diff --git a/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/driver/gl/gl/cogl-driver-gl.c index 25a550c43..6705159ee 100644 --- a/cogl/driver/gl/gl/cogl-driver-gl.c +++ b/cogl/driver/gl/gl/cogl-driver-gl.c @@ -569,11 +569,23 @@ _cogl_driver_update_features (CoglContext *ctx, if (ctx->glGenSamplers) private_flags |= COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS; + if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 3, 3) || _cogl_check_extension ("GL_ARB_texture_swizzle", gl_extensions) || _cogl_check_extension ("GL_EXT_texture_swizzle", gl_extensions)) private_flags |= COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE; + /* The per-vertex point size is only available via GLSL with the + * gl_PointSize builtin. This is only available in GL 2.0 (not the + * GLSL extensions) */ + if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 2, 0)) + { + COGL_FLAGS_SET (ctx->features, + COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE, + TRUE); + private_flags |= COGL_PRIVATE_FEATURE_ENABLE_PROGRAM_POINT_SIZE; + } + if (ctx->driver == COGL_DRIVER_GL) { int max_clip_planes = 0; diff --git a/cogl/driver/gl/gl/cogl-pipeline-progend-fixed-arbfp.c b/cogl/driver/gl/gl/cogl-pipeline-progend-fixed-arbfp.c index 3975b8dd7..fe46a0d0f 100644 --- a/cogl/driver/gl/gl/cogl-pipeline-progend-fixed-arbfp.c +++ b/cogl/driver/gl/gl/cogl-pipeline-progend-fixed-arbfp.c @@ -73,6 +73,11 @@ _cogl_pipeline_progend_fixed_arbfp_start (CoglPipeline *pipeline) _cogl_program_get_language (user_program) != COGL_SHADER_LANGUAGE_ARBFP) return FALSE; + /* The ARBfp progend can't handle the per-vertex point size + * attribute */ + if (cogl_pipeline_get_per_vertex_point_size (pipeline)) + return FALSE; + return TRUE; } diff --git a/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/driver/gl/gles/cogl-driver-gles.c index f34f0b8a2..bdeaa3078 100644 --- a/cogl/driver/gl/gles/cogl-driver-gles.c +++ b/cogl/driver/gl/gles/cogl-driver-gles.c @@ -256,6 +256,8 @@ _cogl_driver_update_features (CoglContext *context, COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_DEPTH_RANGE, TRUE); COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_MIRRORED_REPEAT, TRUE); + COGL_FLAGS_SET (context->features, + COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE, TRUE); private_flags |= COGL_PRIVATE_FEATURE_BLEND_CONSTANT; } diff --git a/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt index be5b99a29..be9bfce24 100644 --- a/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt +++ b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt @@ -834,6 +834,8 @@ cogl_pipeline_set_blend cogl_pipeline_set_blend_constant cogl_pipeline_set_point_size cogl_pipeline_get_point_size +cogl_pipeline_set_per_vertex_point_size +cogl_pipeline_get_per_vertex_point_size cogl_pipeline_get_color_mask cogl_pipeline_set_color_mask diff --git a/examples/cogl-info.c b/examples/cogl-info.c index 37c964bd7..0eb711e1c 100644 --- a/examples/cogl-info.c +++ b/examples/cogl-info.c @@ -115,6 +115,12 @@ struct { "Depth Textures", "CoglFramebuffers can be configured to render their depth buffer into " "a texture" + }, + { + COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE, + "Per-vertex point size", + "cogl_point_size_in can be used as an attribute to specify a per-vertex " + "point size" } }; diff --git a/test-fixtures/test-utils.c b/test-fixtures/test-utils.c index 2c92007cf..5980eeb54 100644 --- a/test-fixtures/test-utils.c +++ b/test-fixtures/test-utils.c @@ -49,6 +49,12 @@ check_flags (TestFlags flags, return FALSE; } + if (flags & TEST_REQUIREMENT_PER_VERTEX_POINT_SIZE && + !cogl_has_feature (test_ctx, COGL_FEATURE_ID_PER_VERTEX_POINT_SIZE)) + { + return FALSE; + } + if (flags & TEST_REQUIREMENT_GLES2_CONTEXT && !cogl_has_feature (test_ctx, COGL_FEATURE_ID_GLES2_CONTEXT)) { diff --git a/test-fixtures/test-utils.h b/test-fixtures/test-utils.h index b5ecdd10e..f7cb74874 100644 --- a/test-fixtures/test-utils.h +++ b/test-fixtures/test-utils.h @@ -22,7 +22,8 @@ typedef enum _TestFlags TEST_REQUIREMENT_MAP_WRITE = 1<<7, TEST_REQUIREMENT_GLSL = 1<<8, TEST_REQUIREMENT_OFFSCREEN = 1<<9, - TEST_REQUIREMENT_FENCE = 1<<10 + TEST_REQUIREMENT_FENCE = 1<<10, + TEST_REQUIREMENT_PER_VERTEX_POINT_SIZE = 1<<11 } TestFlags; extern CoglContext *test_ctx; diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 2f0291ad4..4277aa179 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -48,6 +48,7 @@ test_sources = \ test-read-texture-formats.c \ test-write-texture-formats.c \ test-point-size.c \ + test-point-size-attribute.c \ test-point-sprite.c \ test-no-gl-header.c \ test-version.c \ diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 2c776601d..f8124ed35 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -102,6 +102,11 @@ main (int argc, char **argv) 0); ADD_TEST (test_point_size, 0, 0); + ADD_TEST (test_point_size_attribute, + TEST_REQUIREMENT_PER_VERTEX_POINT_SIZE, 0); + ADD_TEST (test_point_size_attribute_snippet, + TEST_REQUIREMENT_PER_VERTEX_POINT_SIZE | + TEST_REQUIREMENT_GLSL, 0); ADD_TEST (test_point_sprite, TEST_REQUIREMENT_POINT_SPRITE, 0); diff --git a/tests/conform/test-point-size-attribute.c b/tests/conform/test-point-size-attribute.c new file mode 100644 index 000000000..81cae95bb --- /dev/null +++ b/tests/conform/test-point-size-attribute.c @@ -0,0 +1,166 @@ +#include + +#include "test-utils.h" + +/* This test assumes the GL driver supports point sizes up to 16 + pixels. Cogl should probably have some way of querying the size so + we start from that instead */ +#define MAX_POINT_SIZE 16 +#define MIN_POINT_SIZE 4 +#define N_POINTS (MAX_POINT_SIZE - MIN_POINT_SIZE + 1) +/* The size of the area that we'll paint each point in */ +#define POINT_BOX_SIZE (MAX_POINT_SIZE * 2) + +typedef struct +{ + float x, y; + float point_size; +} PointVertex; + +static int +calc_coord_offset (int pos, int pos_index, int point_size) +{ + switch (pos_index) + { + case 0: return pos - point_size / 2 - 2; + case 1: return pos - point_size / 2 + 2; + case 2: return pos + point_size / 2 - 2; + case 3: return pos + point_size / 2 + 2; + } + + g_assert_not_reached (); +} + +static void +verify_point_size (CoglFramebuffer *test_fb, + int x_pos, + int y_pos, + int point_size) +{ + int y, x; + + for (y = 0; y < 4; y++) + for (x = 0; x < 4; x++) + { + CoglBool in_point = x >= 1 && x <= 2 && y >= 1 && y <= 2; + uint32_t expected_pixel = in_point ? 0x00ff00ff : 0xff0000ff; + + test_utils_check_pixel (test_fb, + calc_coord_offset (x_pos, x, point_size), + calc_coord_offset (y_pos, y, point_size), + expected_pixel); + } +} + +static CoglPrimitive * +create_primitive (const char *attribute_name) +{ + PointVertex vertices[N_POINTS]; + CoglAttributeBuffer *buffer; + CoglAttribute *attributes[2]; + CoglPrimitive *prim; + int i; + + for (i = 0; i < N_POINTS; i++) + { + vertices[i].x = i * POINT_BOX_SIZE + POINT_BOX_SIZE / 2; + vertices[i].y = POINT_BOX_SIZE / 2; + vertices[i].point_size = MAX_POINT_SIZE - i; + } + + buffer = cogl_attribute_buffer_new (test_ctx, + sizeof (vertices), + vertices); + + attributes[0] = cogl_attribute_new (buffer, + "cogl_position_in", + sizeof (PointVertex), + G_STRUCT_OFFSET (PointVertex, x), + 2, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + attributes[1] = cogl_attribute_new (buffer, + attribute_name, + sizeof (PointVertex), + G_STRUCT_OFFSET (PointVertex, point_size), + 1, /* n_components */ + COGL_ATTRIBUTE_TYPE_FLOAT); + + prim = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_POINTS, + N_POINTS, + attributes, + 2 /* n_attributes */); + + for (i = 0; i < 2; i++) + cogl_object_unref (attributes[i]); + + return prim; +} + +static void +do_test (const char *attribute_name, + void (* pipeline_setup_func) (CoglPipeline *pipeline)) +{ + int fb_width = cogl_framebuffer_get_width (test_fb); + int fb_height = cogl_framebuffer_get_height (test_fb); + CoglPrimitive *primitive; + CoglPipeline *pipeline; + int i; + + cogl_framebuffer_orthographic (test_fb, + 0, 0, /* x_1, y_1 */ + fb_width, /* x_2 */ + fb_height /* y_2 */, + -1, 100 /* near/far */); + + cogl_framebuffer_clear4f (test_fb, + COGL_BUFFER_BIT_COLOR, + 1.0f, 0.0f, 0.0f, 1.0f); + + primitive = create_primitive (attribute_name); + pipeline = cogl_pipeline_new (test_ctx); + cogl_pipeline_set_color4ub (pipeline, 0x00, 0xff, 0x00, 0xff); + cogl_pipeline_set_per_vertex_point_size (pipeline, TRUE, NULL); + if (pipeline_setup_func) + pipeline_setup_func (pipeline); + cogl_framebuffer_draw_primitive (test_fb, pipeline, primitive); + cogl_object_unref (pipeline); + cogl_object_unref (primitive); + + /* Verify all of the points where drawn at the right size */ + for (i = 0; i < N_POINTS; i++) + verify_point_size (test_fb, + i * POINT_BOX_SIZE + POINT_BOX_SIZE / 2, /* x */ + POINT_BOX_SIZE / 2, /* y */ + MAX_POINT_SIZE - i /* point size */); + + if (cogl_test_verbose ()) + g_print ("OK\n"); +} + +void +test_point_size_attribute (void) +{ + do_test ("cogl_point_size_in", NULL); +} + +static void +setup_snippet (CoglPipeline *pipeline) +{ + CoglSnippet *snippet; + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_POINT_SIZE, + "attribute float " + "my_super_duper_point_size_attrib;\n", + NULL); + cogl_snippet_set_replace (snippet, + "cogl_point_size_out = " + "my_super_duper_point_size_attrib;\n"); + cogl_pipeline_add_snippet (pipeline, snippet); + cogl_object_unref (snippet); +} + +void +test_point_size_attribute_snippet (void) +{ + do_test ("my_super_duper_point_size_attrib", setup_snippet); +}