diff --git a/cogl/Makefile.am b/cogl/Makefile.am index 21d667691..8b11e7647 100644 --- a/cogl/Makefile.am +++ b/cogl/Makefile.am @@ -288,6 +288,8 @@ cogl_sources_c = \ $(srcdir)/cogl-material-compat.c \ $(srcdir)/cogl-program.c \ $(srcdir)/cogl-program-private.h \ + $(srcdir)/cogl-sampler-cache.c \ + $(srcdir)/cogl-sampler-cache-private.h \ $(srcdir)/cogl-blend-string.c \ $(srcdir)/cogl-blend-string.h \ $(srcdir)/cogl-debug.c \ diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h index a79672fa5..ccc5b6d24 100644 --- a/cogl/cogl-context-private.h +++ b/cogl/cogl-context-private.h @@ -47,6 +47,7 @@ #include "cogl-texture-2d.h" #include "cogl-texture-3d.h" #include "cogl-texture-rectangle.h" +#include "cogl-sampler-cache-private.h" typedef struct { @@ -252,6 +253,8 @@ struct _CoglContext CoglWinsysRectangleState rectangle_state; + CoglSamplerCache *sampler_cache; + /* FIXME: remove these when we remove the last xlib based clutter * backend. they should be tracked as part of the renderer but e.g. * the eglx backend doesn't yet have a corresponding Cogl winsys diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index 40d6de014..930f4b83b 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -252,6 +252,8 @@ cogl_context_new (CoglDisplay *display, /* Initialise the driver specific state */ _cogl_init_feature_overrides (context); + _context->sampler_cache = _cogl_sampler_cache_new (_context); + _cogl_pipeline_init_default_pipeline (); _cogl_pipeline_init_default_layers (); _cogl_pipeline_init_state_hash_functions (); @@ -522,6 +524,7 @@ _cogl_context_free (CoglContext *context) cogl_pipeline_cache_free (context->pipeline_cache); + _cogl_sampler_cache_free (context->sampler_cache); _cogl_destroy_texture_units (); diff --git a/cogl/cogl-internal.h b/cogl/cogl-internal.h index 284fc974c..89d725894 100644 --- a/cogl/cogl-internal.h +++ b/cogl/cogl-internal.h @@ -102,7 +102,8 @@ typedef enum COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL = 1L<<7, COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL = 1L<<8, COGL_PRIVATE_FEATURE_TEXTURE_FORMAT_BGRA8888 = 1L<<9, - COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE = 1L<<10 + COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE = 1L<<10, + COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS = 1L<<11 } CoglPrivateFeatureFlags; /* Sometimes when evaluating pipelines, either during comparisons or diff --git a/cogl/cogl-pipeline-layer-private.h b/cogl/cogl-pipeline-layer-private.h index 4446224ba..6925b40c1 100644 --- a/cogl/cogl-pipeline-layer-private.h +++ b/cogl/cogl-pipeline-layer-private.h @@ -35,6 +35,7 @@ #include "cogl-pipeline-layer-state.h" #include "cogl-internal.h" #include "cogl-pipeline-snippet-private.h" +#include "cogl-sampler-cache-private.h" #include @@ -49,21 +50,6 @@ typedef struct _CoglPipelineLayer CoglPipelineLayer; #define COGL_PIPELINE_LAYER(OBJECT) ((CoglPipelineLayer *)OBJECT) -/* GL_ALWAYS is just used here as a value that is known not to clash - * with any valid GL wrap modes. - * - * XXX: keep the values in sync with the CoglPipelineWrapMode enum - * so no conversion is actually needed. - */ -typedef enum _CoglPipelineWrapModeInternal -{ - COGL_PIPELINE_WRAP_MODE_INTERNAL_REPEAT = GL_REPEAT, - COGL_PIPELINE_WRAP_MODE_INTERNAL_MIRRORED_REPEAT = GL_MIRRORED_REPEAT, - COGL_PIPELINE_WRAP_MODE_INTERNAL_CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE, - COGL_PIPELINE_WRAP_MODE_INTERNAL_CLAMP_TO_BORDER = GL_CLAMP_TO_BORDER, - COGL_PIPELINE_WRAP_MODE_INTERNAL_AUTOMATIC = GL_ALWAYS -} CoglPipelineWrapModeInternal; - /* XXX: should I rename these as * COGL_PIPELINE_LAYER_STATE_INDEX_XYZ... ? */ @@ -73,8 +59,7 @@ typedef enum COGL_PIPELINE_LAYER_STATE_UNIT_INDEX, COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE_INDEX, COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA_INDEX, - COGL_PIPELINE_LAYER_STATE_FILTERS_INDEX, - COGL_PIPELINE_LAYER_STATE_WRAP_MODES_INDEX, + COGL_PIPELINE_LAYER_STATE_SAMPLER_INDEX, COGL_PIPELINE_LAYER_STATE_COMBINE_INDEX, COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT_INDEX, COGL_PIPELINE_LAYER_STATE_USER_MATRIX_INDEX, @@ -103,10 +88,8 @@ typedef enum 1L<wrap_mode_s == wrap_mode_s && - authority->wrap_mode_t == wrap_mode_t && - authority->wrap_mode_p == wrap_mode_p) + if (authority->sampler_cache_entry == state) return; new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); @@ -394,9 +390,7 @@ _cogl_pipeline_set_layer_wrap_modes (CoglPipeline *pipeline, CoglPipelineLayer *old_authority = _cogl_pipeline_layer_get_authority (parent, change); - if (old_authority->wrap_mode_s == wrap_mode_s && - old_authority->wrap_mode_t == wrap_mode_t && - old_authority->wrap_mode_p == wrap_mode_p) + if (old_authority->sampler_cache_entry == state) { layer->differences &= ~change; @@ -409,9 +403,7 @@ _cogl_pipeline_set_layer_wrap_modes (CoglPipeline *pipeline, } } - layer->wrap_mode_s = wrap_mode_s; - layer->wrap_mode_t = wrap_mode_t; - layer->wrap_mode_p = wrap_mode_p; + layer->sampler_cache_entry = state; /* If we weren't previously the authority on this state then we need * to extended our differences mask and so it's possible that some @@ -424,17 +416,17 @@ _cogl_pipeline_set_layer_wrap_modes (CoglPipeline *pipeline, } } -static CoglPipelineWrapModeInternal +static CoglSamplerCacheWrapMode public_to_internal_wrap_mode (CoglPipelineWrapMode mode) { - return (CoglPipelineWrapModeInternal)mode; + return (CoglSamplerCacheWrapMode)mode; } static CoglPipelineWrapMode -internal_to_public_wrap_mode (CoglPipelineWrapModeInternal internal_mode) +internal_to_public_wrap_mode (CoglSamplerCacheWrapMode internal_mode) { _COGL_RETURN_VAL_IF_FAIL (internal_mode != - COGL_PIPELINE_WRAP_MODE_INTERNAL_CLAMP_TO_BORDER, + COGL_SAMPLER_CACHE_WRAP_MODE_CLAMP_TO_BORDER, COGL_PIPELINE_WRAP_MODE_AUTOMATIC); return (CoglPipelineWrapMode)internal_mode; } @@ -444,11 +436,14 @@ cogl_pipeline_set_layer_wrap_mode_s (CoglPipeline *pipeline, int layer_index, CoglPipelineWrapMode mode) { - CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES; + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; CoglPipelineLayer *layer; CoglPipelineLayer *authority; - CoglPipelineWrapModeInternal internal_mode = + CoglSamplerCacheWrapMode internal_mode = public_to_internal_wrap_mode (mode); + const CoglSamplerCacheEntry *sampler_state; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); @@ -464,22 +459,33 @@ cogl_pipeline_set_layer_wrap_mode_s (CoglPipeline *pipeline, * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, change); - _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority, - internal_mode, - authority->wrap_mode_t, - authority->wrap_mode_p); + sampler_state = + _cogl_sampler_cache_update_wrap_modes (ctx->sampler_cache, + authority->sampler_cache_entry, + internal_mode, + authority->sampler_cache_entry-> + wrap_mode_t, + authority->sampler_cache_entry-> + wrap_mode_p); + _cogl_pipeline_set_layer_sampler_state (pipeline, + layer, + authority, + sampler_state); } void -cogl_pipeline_set_layer_wrap_mode_t (CoglPipeline *pipeline, - int layer_index, +cogl_pipeline_set_layer_wrap_mode_t (CoglPipeline *pipeline, + int layer_index, CoglPipelineWrapMode mode) { - CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES; + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; CoglPipelineLayer *layer; CoglPipelineLayer *authority; - CoglPipelineWrapModeInternal internal_mode = + CoglSamplerCacheWrapMode internal_mode = public_to_internal_wrap_mode (mode); + const CoglSamplerCacheEntry *sampler_state; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); @@ -495,10 +501,18 @@ cogl_pipeline_set_layer_wrap_mode_t (CoglPipeline *pipeline, * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, change); - _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority, - authority->wrap_mode_s, - internal_mode, - authority->wrap_mode_p); + sampler_state = + _cogl_sampler_cache_update_wrap_modes (ctx->sampler_cache, + authority->sampler_cache_entry, + authority->sampler_cache_entry-> + wrap_mode_s, + internal_mode, + authority->sampler_cache_entry-> + wrap_mode_p); + _cogl_pipeline_set_layer_sampler_state (pipeline, + layer, + authority, + sampler_state); } /* The rationale for naming the third texture coordinate 'p' instead @@ -514,15 +528,18 @@ cogl_pipeline_set_layer_wrap_mode_t (CoglPipeline *pipeline, the w component conflicts with the w component of a position vertex. */ void -cogl_pipeline_set_layer_wrap_mode_p (CoglPipeline *pipeline, - int layer_index, +cogl_pipeline_set_layer_wrap_mode_p (CoglPipeline *pipeline, + int layer_index, CoglPipelineWrapMode mode) { - CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES; + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; CoglPipelineLayer *layer; CoglPipelineLayer *authority; - CoglPipelineWrapModeInternal internal_mode = + CoglSamplerCacheWrapMode internal_mode = public_to_internal_wrap_mode (mode); + const CoglSamplerCacheEntry *sampler_state; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); @@ -538,22 +555,33 @@ cogl_pipeline_set_layer_wrap_mode_p (CoglPipeline *pipeline, * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, change); - _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority, - authority->wrap_mode_s, - authority->wrap_mode_t, - internal_mode); + sampler_state = + _cogl_sampler_cache_update_wrap_modes (ctx->sampler_cache, + authority->sampler_cache_entry, + authority->sampler_cache_entry-> + wrap_mode_s, + authority->sampler_cache_entry-> + wrap_mode_t, + internal_mode); + _cogl_pipeline_set_layer_sampler_state (pipeline, + layer, + authority, + sampler_state); } void -cogl_pipeline_set_layer_wrap_mode (CoglPipeline *pipeline, - int layer_index, +cogl_pipeline_set_layer_wrap_mode (CoglPipeline *pipeline, + int layer_index, CoglPipelineWrapMode mode) { - CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES; + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; CoglPipelineLayer *layer; CoglPipelineLayer *authority; - CoglPipelineWrapModeInternal internal_mode = + CoglSamplerCacheWrapMode internal_mode = public_to_internal_wrap_mode (mode); + const CoglSamplerCacheEntry *sampler_state; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); @@ -569,20 +597,27 @@ cogl_pipeline_set_layer_wrap_mode (CoglPipeline *pipeline, * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, change); - _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority, - internal_mode, - internal_mode, - internal_mode); + sampler_state = + _cogl_sampler_cache_update_wrap_modes (ctx->sampler_cache, + authority->sampler_cache_entry, + internal_mode, + internal_mode, + internal_mode); + _cogl_pipeline_set_layer_sampler_state (pipeline, + layer, + authority, + sampler_state); /* XXX: I wonder if we should really be duplicating the mode into - * the 'r' wrap mode too? */ + * the 'p' wrap mode too? */ } /* FIXME: deprecate this API */ CoglPipelineWrapMode _cogl_pipeline_layer_get_wrap_mode_s (CoglPipelineLayer *layer) { - CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES; + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; CoglPipelineLayer *authority; + const CoglSamplerCacheEntry *sampler_state; _COGL_RETURN_VAL_IF_FAIL (_cogl_is_pipeline_layer (layer), FALSE); @@ -590,7 +625,8 @@ _cogl_pipeline_layer_get_wrap_mode_s (CoglPipelineLayer *layer) * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, change); - return internal_to_public_wrap_mode (authority->wrap_mode_s); + sampler_state = authority->sampler_cache_entry; + return internal_to_public_wrap_mode (sampler_state->wrap_mode_s); } CoglPipelineWrapMode @@ -616,8 +652,9 @@ cogl_pipeline_get_layer_wrap_mode_s (CoglPipeline *pipeline, int layer_index) CoglPipelineWrapMode _cogl_pipeline_layer_get_wrap_mode_t (CoglPipelineLayer *layer) { - CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES; + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; CoglPipelineLayer *authority; + const CoglSamplerCacheEntry *sampler_state; _COGL_RETURN_VAL_IF_FAIL (_cogl_is_pipeline_layer (layer), FALSE); @@ -625,7 +662,8 @@ _cogl_pipeline_layer_get_wrap_mode_t (CoglPipelineLayer *layer) * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, change); - return internal_to_public_wrap_mode (authority->wrap_mode_t); + sampler_state = authority->sampler_cache_entry; + return internal_to_public_wrap_mode (sampler_state->wrap_mode_t); } CoglPipelineWrapMode @@ -650,11 +688,13 @@ cogl_pipeline_get_layer_wrap_mode_t (CoglPipeline *pipeline, int layer_index) CoglPipelineWrapMode _cogl_pipeline_layer_get_wrap_mode_p (CoglPipelineLayer *layer) { - CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES; + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; CoglPipelineLayer *authority = _cogl_pipeline_layer_get_authority (layer, change); + const CoglSamplerCacheEntry *sampler_state; - return internal_to_public_wrap_mode (authority->wrap_mode_p); + sampler_state = authority->sampler_cache_entry; + return internal_to_public_wrap_mode (sampler_state->wrap_mode_p); } CoglPipelineWrapMode @@ -677,17 +717,17 @@ cogl_pipeline_get_layer_wrap_mode_p (CoglPipeline *pipeline, int layer_index) void _cogl_pipeline_layer_get_wrap_modes (CoglPipelineLayer *layer, - CoglPipelineWrapModeInternal *wrap_mode_s, - CoglPipelineWrapModeInternal *wrap_mode_t, - CoglPipelineWrapModeInternal *wrap_mode_p) + CoglSamplerCacheWrapMode *wrap_mode_s, + CoglSamplerCacheWrapMode *wrap_mode_t, + CoglSamplerCacheWrapMode *wrap_mode_p) { CoglPipelineLayer *authority = _cogl_pipeline_layer_get_authority (layer, - COGL_PIPELINE_LAYER_STATE_WRAP_MODES); + COGL_PIPELINE_LAYER_STATE_SAMPLER); - *wrap_mode_s = authority->wrap_mode_s; - *wrap_mode_t = authority->wrap_mode_t; - *wrap_mode_p = authority->wrap_mode_p; + *wrap_mode_s = authority->sampler_cache_entry->wrap_mode_s; + *wrap_mode_t = authority->sampler_cache_entry->wrap_mode_t; + *wrap_mode_p = authority->sampler_cache_entry->wrap_mode_p; } gboolean @@ -989,45 +1029,14 @@ _cogl_pipeline_layer_combine_constant_equal (CoglPipelineLayer *authority0, } gboolean -_cogl_pipeline_layer_filters_equal (CoglPipelineLayer *authority0, +_cogl_pipeline_layer_sampler_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1) { - if (authority0->mag_filter != authority1->mag_filter) - return FALSE; - if (authority0->min_filter != authority1->min_filter) - return FALSE; - - return TRUE; -} - -static gboolean -compare_wrap_mode_equal (CoglPipelineWrapMode wrap_mode0, - CoglPipelineWrapMode wrap_mode1) -{ - /* We consider AUTOMATIC to be equivalent to CLAMP_TO_EDGE because - the primitives code is expected to override this to something - else if it wants it to be behave any other way */ - if (wrap_mode0 == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) - wrap_mode0 = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; - if (wrap_mode1 == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) - wrap_mode1 = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; - - return wrap_mode0 == wrap_mode1; -} - -gboolean -_cogl_pipeline_layer_wrap_modes_equal (CoglPipelineLayer *authority0, - CoglPipelineLayer *authority1) -{ - if (!compare_wrap_mode_equal (authority0->wrap_mode_s, - authority1->wrap_mode_s) || - !compare_wrap_mode_equal (authority0->wrap_mode_t, - authority1->wrap_mode_t) || - !compare_wrap_mode_equal (authority0->wrap_mode_p, - authority1->wrap_mode_p)) - return FALSE; - - return TRUE; + /* We compare the actual sampler objects rather than just the entry + pointers because two states with different values can lead to the + same state in GL terms when AUTOMATIC is used as a wrap mode */ + return (authority0->sampler_cache_entry->sampler_object == + authority1->sampler_cache_entry->sampler_object); } gboolean @@ -1500,10 +1509,10 @@ _cogl_pipeline_layer_get_filters (CoglPipelineLayer *layer, { CoglPipelineLayer *authority = _cogl_pipeline_layer_get_authority (layer, - COGL_PIPELINE_LAYER_STATE_FILTERS); + COGL_PIPELINE_LAYER_STATE_SAMPLER); - *min_filter = authority->min_filter; - *mag_filter = authority->mag_filter; + *min_filter = authority->sampler_cache_entry->min_filter; + *mag_filter = authority->sampler_cache_entry->mag_filter; } void @@ -1521,10 +1530,10 @@ _cogl_pipeline_get_layer_filters (CoglPipeline *pipeline, authority = _cogl_pipeline_layer_get_authority (layer, - COGL_PIPELINE_LAYER_STATE_FILTERS); + COGL_PIPELINE_LAYER_STATE_SAMPLER); - *min_filter = authority->min_filter; - *mag_filter = authority->mag_filter; + *min_filter = authority->sampler_cache_entry->min_filter; + *mag_filter = authority->sampler_cache_entry->mag_filter; } CoglPipelineFilter @@ -1560,9 +1569,9 @@ _cogl_pipeline_layer_get_min_filter (CoglPipelineLayer *layer) authority = _cogl_pipeline_layer_get_authority (layer, - COGL_PIPELINE_LAYER_STATE_FILTERS); + COGL_PIPELINE_LAYER_STATE_SAMPLER); - return authority->min_filter; + return authority->sampler_cache_entry->min_filter; } CoglPipelineFilter @@ -1574,9 +1583,9 @@ _cogl_pipeline_layer_get_mag_filter (CoglPipelineLayer *layer) authority = _cogl_pipeline_layer_get_authority (layer, - COGL_PIPELINE_LAYER_STATE_FILTERS); + COGL_PIPELINE_LAYER_STATE_SAMPLER); - return authority->mag_filter; + return authority->sampler_cache_entry->mag_filter; } void @@ -1585,10 +1594,12 @@ cogl_pipeline_set_layer_filters (CoglPipeline *pipeline, CoglPipelineFilter min_filter, CoglPipelineFilter mag_filter) { - CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_FILTERS; - CoglPipelineLayer *layer; - CoglPipelineLayer *authority; - CoglPipelineLayer *new; + CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_SAMPLER; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + const CoglSamplerCacheEntry *sampler_state; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline)); @@ -1604,52 +1615,27 @@ cogl_pipeline_set_layer_filters (CoglPipeline *pipeline, * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, state); - if (authority->min_filter == min_filter && - authority->mag_filter == mag_filter) - return; + sampler_state = + _cogl_sampler_cache_update_filters (ctx->sampler_cache, + authority->sampler_cache_entry, + min_filter, + mag_filter); + _cogl_pipeline_set_layer_sampler_state (pipeline, + layer, + authority, + sampler_state); +} - new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state); - if (new != layer) - layer = new; - else - { - /* If the original layer we found is currently the authority on - * the state we are changing see if we can revert to one of our - * ancestors being the authority. */ - if (layer == authority && - _cogl_pipeline_layer_get_parent (authority) != NULL) - { - CoglPipelineLayer *parent = - _cogl_pipeline_layer_get_parent (authority); - CoglPipelineLayer *old_authority = - _cogl_pipeline_layer_get_authority (parent, state); +const CoglSamplerCacheEntry * +_cogl_pipeline_layer_get_sampler_state (CoglPipelineLayer *layer) +{ + CoglPipelineLayer *authority; - if (old_authority->min_filter == min_filter && - old_authority->mag_filter == mag_filter) - { - layer->differences &= ~state; + authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_SAMPLER); - g_assert (layer->owner == pipeline); - if (layer->differences == 0) - _cogl_pipeline_prune_empty_layer_difference (pipeline, - layer); - return; - } - } - } - - layer->min_filter = min_filter; - layer->mag_filter = mag_filter; - - /* If we weren't previously the authority on this state then we need - * to extended our differences mask and so it's possible that some - * of our ancestry will now become redundant, so we aim to reparent - * ourselves if that's true... */ - if (layer != authority) - { - layer->differences |= state; - _cogl_pipeline_layer_prune_redundant_ancestry (layer); - } + return authority->sampler_cache_entry; } void @@ -1688,31 +1674,14 @@ _cogl_pipeline_layer_hash_texture_data_state (CoglPipelineLayer *authority, } void -_cogl_pipeline_layer_hash_filters_state (CoglPipelineLayer *authority, +_cogl_pipeline_layer_hash_sampler_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state) { - unsigned int hash = state->hash; - hash = _cogl_util_one_at_a_time_hash (hash, &authority->mag_filter, - sizeof (authority->mag_filter)); - hash = _cogl_util_one_at_a_time_hash (hash, &authority->min_filter, - sizeof (authority->min_filter)); - state->hash = hash; -} - -void -_cogl_pipeline_layer_hash_wrap_modes_state (CoglPipelineLayer *authority, - CoglPipelineLayer **authorities, - CoglPipelineHashState *state) -{ - unsigned int hash = state->hash; - hash = _cogl_util_one_at_a_time_hash (hash, &authority->wrap_mode_s, - sizeof (authority->wrap_mode_s)); - hash = _cogl_util_one_at_a_time_hash (hash, &authority->wrap_mode_t, - sizeof (authority->wrap_mode_t)); - hash = _cogl_util_one_at_a_time_hash (hash, &authority->wrap_mode_p, - sizeof (authority->wrap_mode_p)); - state->hash = hash; + state->hash = + _cogl_util_one_at_a_time_hash (state->hash, + &authority->sampler_cache_entry, + sizeof (authority->sampler_cache_entry)); } void diff --git a/cogl/cogl-pipeline-layer.c b/cogl/cogl-pipeline-layer.c index 558a8b616..9093a0434 100644 --- a/cogl/cogl-pipeline-layer.c +++ b/cogl/cogl-pipeline-layer.c @@ -170,21 +170,13 @@ _cogl_pipeline_layer_init_multi_property_sparse_state ( case COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS: case COGL_PIPELINE_LAYER_STATE_USER_MATRIX: case COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT: + case COGL_PIPELINE_LAYER_STATE_SAMPLER: g_return_if_reached (); /* XXX: technically we could probably even consider these as * single property state-groups from the pov that currently the * corresponding property setters always update all of the values * at the same time. */ - case COGL_PIPELINE_LAYER_STATE_FILTERS: - layer->min_filter = authority->min_filter; - layer->mag_filter = authority->mag_filter; - break; - case COGL_PIPELINE_LAYER_STATE_WRAP_MODES: - layer->wrap_mode_s = authority->wrap_mode_s; - layer->wrap_mode_t = authority->wrap_mode_t; - layer->wrap_mode_p = authority->wrap_mode_p; - break; case COGL_PIPELINE_LAYER_STATE_COMBINE: { int n_args; @@ -576,16 +568,10 @@ _cogl_pipeline_layer_equal (CoglPipelineLayer *layer0, _cogl_pipeline_layer_combine_constant_equal)) return FALSE; - if (layers_difference & COGL_PIPELINE_LAYER_STATE_FILTERS && - !layer_state_equal (COGL_PIPELINE_LAYER_STATE_FILTERS_INDEX, + if (layers_difference & COGL_PIPELINE_LAYER_STATE_SAMPLER && + !layer_state_equal (COGL_PIPELINE_LAYER_STATE_SAMPLER_INDEX, authorities0, authorities1, - _cogl_pipeline_layer_filters_equal)) - return FALSE; - - if (layers_difference & COGL_PIPELINE_LAYER_STATE_WRAP_MODES && - !layer_state_equal (COGL_PIPELINE_LAYER_STATE_WRAP_MODES_INDEX, - authorities0, authorities1, - _cogl_pipeline_layer_wrap_modes_equal)) + _cogl_pipeline_layer_sampler_equal)) return FALSE; if (layers_difference & COGL_PIPELINE_LAYER_STATE_USER_MATRIX && @@ -657,12 +643,8 @@ _cogl_pipeline_init_default_layers (void) layer->texture = NULL; layer->texture_type = COGL_TEXTURE_TYPE_2D; - layer->mag_filter = COGL_PIPELINE_FILTER_LINEAR; - layer->min_filter = COGL_PIPELINE_FILTER_LINEAR; - - layer->wrap_mode_s = COGL_PIPELINE_WRAP_MODE_AUTOMATIC; - layer->wrap_mode_t = COGL_PIPELINE_WRAP_MODE_AUTOMATIC; - layer->wrap_mode_p = COGL_PIPELINE_WRAP_MODE_AUTOMATIC; + layer->sampler_cache_entry = + _cogl_sampler_cache_get_default_entry (ctx->sampler_cache); layer->big_state = big_state; layer->has_big_state = TRUE; diff --git a/cogl/cogl-pipeline-opengl.c b/cogl/cogl-pipeline-opengl.c index a76324027..fc331d0f9 100644 --- a/cogl/cogl-pipeline-opengl.c +++ b/cogl/cogl-pipeline-opengl.c @@ -844,6 +844,16 @@ flush_layers_common_gl_state_cb (CoglPipelineLayer *layer, void *user_data) unit->texture_storage_changed = FALSE; } + if ((layers_difference & COGL_PIPELINE_LAYER_STATE_SAMPLER) && + (ctx->private_feature_flags & COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS)) + { + const CoglSamplerCacheEntry *sampler_state; + + sampler_state = _cogl_pipeline_layer_get_sampler_state (layer); + + GE( ctx, glBindSampler (unit_index, sampler_state->sampler_object) ); + } + /* Under GLES2 the fragment shader will use gl_PointCoord instead of replacing the texture coordinates */ #if defined (HAVE_COGL_GLES) || defined (HAVE_COGL_GL) @@ -904,7 +914,7 @@ static void _cogl_pipeline_layer_forward_wrap_modes (CoglPipelineLayer *layer, CoglTexture *texture) { - CoglPipelineWrapModeInternal wrap_mode_s, wrap_mode_t, wrap_mode_p; + CoglSamplerCacheWrapMode wrap_mode_s, wrap_mode_t, wrap_mode_p; GLenum gl_wrap_mode_s, gl_wrap_mode_t, gl_wrap_mode_p; if (texture == NULL) @@ -926,17 +936,17 @@ _cogl_pipeline_layer_forward_wrap_modes (CoglPipelineLayer *layer, will break if the application tries to use different modes in different layers using the same texture. */ - if (wrap_mode_s == COGL_PIPELINE_WRAP_MODE_INTERNAL_AUTOMATIC) + if (wrap_mode_s == COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC) gl_wrap_mode_s = GL_CLAMP_TO_EDGE; else gl_wrap_mode_s = wrap_mode_s; - if (wrap_mode_t == COGL_PIPELINE_WRAP_MODE_INTERNAL_AUTOMATIC) + if (wrap_mode_t == COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC) gl_wrap_mode_t = GL_CLAMP_TO_EDGE; else gl_wrap_mode_t = wrap_mode_t; - if (wrap_mode_p == COGL_PIPELINE_WRAP_MODE_INTERNAL_AUTOMATIC) + if (wrap_mode_p == COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC) gl_wrap_mode_p = GL_CLAMP_TO_EDGE; else gl_wrap_mode_p = wrap_mode_p; @@ -952,8 +962,10 @@ _cogl_pipeline_layer_forward_wrap_modes (CoglPipelineLayer *layer, * the filter and repeat modes whenever we use a texture since it may * be referenced by multiple pipelines with different modes. * - * XXX: GL_ARB_sampler_objects fixes this in OpenGL so we should - * eventually look at using this extension when available. + * This function is bypassed in favour of sampler objects if + * GL_ARB_sampler_objects is advertised. This fallback won't work if + * the same texture is bound to multiple layers with different sampler + * state. */ static void foreach_texture_unit_update_filter_and_wrap_modes (void) @@ -1399,7 +1411,8 @@ done: /* Handle the fact that OpenGL associates texture filter and wrap * modes with the texture objects not the texture units... */ - foreach_texture_unit_update_filter_and_wrap_modes (); + if (!(ctx->private_feature_flags & COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS)) + foreach_texture_unit_update_filter_and_wrap_modes (); /* If this pipeline has more than one layer then we always need * to make sure we rebind the texture for unit 1. diff --git a/cogl/cogl-pipeline.c b/cogl/cogl-pipeline.c index 7ee1b297f..a5f938cb9 100644 --- a/cogl/cogl-pipeline.c +++ b/cogl/cogl-pipeline.c @@ -2572,10 +2572,8 @@ _cogl_pipeline_init_layer_state_hash_functions (void) _cogl_pipeline_layer_hash_texture_type_state; layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA_INDEX] = _cogl_pipeline_layer_hash_texture_data_state; - layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_FILTERS_INDEX] = - _cogl_pipeline_layer_hash_filters_state; - layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_WRAP_MODES_INDEX] = - _cogl_pipeline_layer_hash_wrap_modes_state; + layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_SAMPLER_INDEX] = + _cogl_pipeline_layer_hash_sampler_state; layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_COMBINE_INDEX] = _cogl_pipeline_layer_hash_combine_state; layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT_INDEX] = @@ -2593,7 +2591,7 @@ _cogl_pipeline_init_layer_state_hash_functions (void) _cogl_pipeline_layer_hash_fragment_snippets_state; /* So we get a big error if we forget to update this code! */ - g_assert (COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT == 11); + g_assert (COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT == 10); } static gboolean diff --git a/cogl/cogl-sampler-cache-private.h b/cogl/cogl-sampler-cache-private.h new file mode 100644 index 000000000..4eb750390 --- /dev/null +++ b/cogl/cogl-sampler-cache-private.h @@ -0,0 +1,82 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2012 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * + */ + +#ifndef __COGL_SAMPLER_CACHE_PRIVATE_H +#define __COGL_SAMPLER_CACHE_PRIVATE_H + +#include "cogl-context.h" +#include "cogl-gl-header.h" + +/* GL_ALWAYS is just used here as a value that is known not to clash + * with any valid GL wrap modes. + * + * XXX: keep the values in sync with the CoglPipelineWrapMode enum + * so no conversion is actually needed. + */ +typedef enum _CoglSamplerCacheWrapMode +{ + COGL_SAMPLER_CACHE_WRAP_MODE_REPEAT = GL_REPEAT, + COGL_SAMPLER_CACHE_WRAP_MODE_MIRRORED_REPEAT = GL_MIRRORED_REPEAT, + COGL_SAMPLER_CACHE_WRAP_MODE_CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE, + COGL_SAMPLER_CACHE_WRAP_MODE_CLAMP_TO_BORDER = GL_CLAMP_TO_BORDER, + COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC = GL_ALWAYS +} CoglSamplerCacheWrapMode; + +typedef struct _CoglSamplerCache CoglSamplerCache; + +typedef struct _CoglSamplerCacheEntry +{ + GLuint sampler_object; + + GLenum min_filter; + GLenum mag_filter; + + CoglSamplerCacheWrapMode wrap_mode_s; + CoglSamplerCacheWrapMode wrap_mode_t; + CoglSamplerCacheWrapMode wrap_mode_p; +} CoglSamplerCacheEntry; + +CoglSamplerCache * +_cogl_sampler_cache_new (CoglContext *context); + +const CoglSamplerCacheEntry * +_cogl_sampler_cache_get_default_entry (CoglSamplerCache *cache); + +const CoglSamplerCacheEntry * +_cogl_sampler_cache_update_wrap_modes (CoglSamplerCache *cache, + const CoglSamplerCacheEntry *old_entry, + CoglSamplerCacheWrapMode wrap_mode_s, + CoglSamplerCacheWrapMode wrap_mode_t, + CoglSamplerCacheWrapMode wrap_mode_p); + +const CoglSamplerCacheEntry * +_cogl_sampler_cache_update_filters (CoglSamplerCache *cache, + const CoglSamplerCacheEntry *old_entry, + GLenum min_filter, + GLenum mag_filter); + +void +_cogl_sampler_cache_free (CoglSamplerCache *cache); + +#endif /* __COGL_SAMPLER_CACHE_PRIVATE_H */ diff --git a/cogl/cogl-sampler-cache.c b/cogl/cogl-sampler-cache.c new file mode 100644 index 000000000..aa0d4a423 --- /dev/null +++ b/cogl/cogl-sampler-cache.c @@ -0,0 +1,360 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2012 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-sampler-cache-private.h" +#include "cogl-context-private.h" + +struct _CoglSamplerCache +{ + CoglContext *context; + + /* The samplers are hashed in two tables. One is using the enum + values that Cogl exposes (so it can include the 'automatic' wrap + mode) and the other is using the converted values that will be + given to GL. The first is used to get a unique pointer for the + sampler state so that pipelines only need to store a single + pointer instead of the whole state and the second is used so that + only a single GL sampler object will be created for each unique + GL state. */ + GHashTable *hash_table_cogl; + GHashTable *hash_table_gl; + + /* This is used for generated fake unique sampler object numbers + when the sampler object extension is not supported */ + GLuint next_fake_sampler_object_number; +}; + +static CoglSamplerCacheWrapMode +get_real_wrap_mode (CoglSamplerCacheWrapMode wrap_mode) +{ + if (wrap_mode == COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC) + return COGL_SAMPLER_CACHE_WRAP_MODE_CLAMP_TO_EDGE; + + return wrap_mode; +} + +static void +canonicalize_key (CoglSamplerCacheEntry *key) +{ + /* This converts the wrap modes to the enums that will actually be + given to GL so that it can be used as a key to get a unique GL + sampler object for the state */ + key->wrap_mode_s = get_real_wrap_mode (key->wrap_mode_s); + key->wrap_mode_t = get_real_wrap_mode (key->wrap_mode_t); + key->wrap_mode_p = get_real_wrap_mode (key->wrap_mode_p); +} + +static gboolean +wrap_mode_equal_gl (CoglSamplerCacheWrapMode wrap_mode0, + CoglSamplerCacheWrapMode wrap_mode1) +{ + /* We want to compare the actual GLenum that will be used so that if + two different wrap_modes actually use the same GL state we'll + still use the same sampler object */ + return get_real_wrap_mode (wrap_mode0) == get_real_wrap_mode (wrap_mode1); +} + +static gboolean +sampler_state_equal_gl (const void *value0, + const void *value1) +{ + const CoglSamplerCacheEntry *state0 = value0; + const CoglSamplerCacheEntry *state1 = value1; + + return (state0->mag_filter == state1->mag_filter && + state0->min_filter == state1->min_filter && + wrap_mode_equal_gl (state0->wrap_mode_s, state1->wrap_mode_s) && + wrap_mode_equal_gl (state0->wrap_mode_t, state1->wrap_mode_t) && + wrap_mode_equal_gl (state0->wrap_mode_p, state1->wrap_mode_p)); +} + +static unsigned int +hash_wrap_mode_gl (unsigned int hash, + CoglSamplerCacheWrapMode wrap_mode) +{ + /* We want to hash the actual GLenum that will be used so that if + two different wrap_modes actually use the same GL state we'll + still use the same sampler object */ + GLenum real_wrap_mode = get_real_wrap_mode (wrap_mode); + + return _cogl_util_one_at_a_time_hash (hash, + &real_wrap_mode, + sizeof (real_wrap_mode)); +} + +static unsigned int +hash_sampler_state_gl (const void *key) +{ + const CoglSamplerCacheEntry *entry = key; + unsigned int hash = 0; + + hash = _cogl_util_one_at_a_time_hash (hash, &entry->mag_filter, + sizeof (entry->mag_filter)); + hash = _cogl_util_one_at_a_time_hash (hash, &entry->min_filter, + sizeof (entry->min_filter)); + hash = hash_wrap_mode_gl (hash, entry->wrap_mode_s); + hash = hash_wrap_mode_gl (hash, entry->wrap_mode_t); + hash = hash_wrap_mode_gl (hash, entry->wrap_mode_p); + + return _cogl_util_one_at_a_time_mix (hash); +} + +static gboolean +sampler_state_equal_cogl (const void *value0, + const void *value1) +{ + const CoglSamplerCacheEntry *state0 = value0; + const CoglSamplerCacheEntry *state1 = value1; + + return (state0->mag_filter == state1->mag_filter && + state0->min_filter == state1->min_filter && + state0->wrap_mode_s == state1->wrap_mode_s && + state0->wrap_mode_t == state1->wrap_mode_t && + state0->wrap_mode_p == state1->wrap_mode_p); +} + +static unsigned int +hash_sampler_state_cogl (const void *key) +{ + const CoglSamplerCacheEntry *entry = key; + unsigned int hash = 0; + + hash = _cogl_util_one_at_a_time_hash (hash, &entry->mag_filter, + sizeof (entry->mag_filter)); + hash = _cogl_util_one_at_a_time_hash (hash, &entry->min_filter, + sizeof (entry->min_filter)); + hash = _cogl_util_one_at_a_time_hash (hash, &entry->wrap_mode_s, + sizeof (entry->wrap_mode_s)); + hash = _cogl_util_one_at_a_time_hash (hash, &entry->wrap_mode_t, + sizeof (entry->wrap_mode_t)); + hash = _cogl_util_one_at_a_time_hash (hash, &entry->wrap_mode_p, + sizeof (entry->wrap_mode_p)); + + return _cogl_util_one_at_a_time_mix (hash); +} + +CoglSamplerCache * +_cogl_sampler_cache_new (CoglContext *context) +{ + CoglSamplerCache *cache = g_new (CoglSamplerCache, 1); + + /* No reference is taken on the context because it would create a + circular reference */ + cache->context = context; + + cache->hash_table_gl = g_hash_table_new (hash_sampler_state_gl, + sampler_state_equal_gl); + cache->hash_table_cogl = g_hash_table_new (hash_sampler_state_cogl, + sampler_state_equal_cogl); + cache->next_fake_sampler_object_number = 1; + + return cache; +} + +static void +set_wrap_mode (CoglContext *context, + GLuint sampler_object, + GLenum param, + CoglSamplerCacheWrapMode wrap_mode) +{ + GE( context, glSamplerParameteri (sampler_object, + param, + wrap_mode) ); +} + +static CoglSamplerCacheEntry * +_cogl_sampler_cache_get_entry_gl (CoglSamplerCache *cache, + const CoglSamplerCacheEntry *key) +{ + CoglSamplerCacheEntry *entry; + + entry = g_hash_table_lookup (cache->hash_table_gl, key); + + if (entry == NULL) + { + CoglContext *context = cache->context; + + entry = g_slice_dup (CoglSamplerCacheEntry, key); + + if ((context->private_feature_flags & + COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS)) + { + GE( context, glGenSamplers (1, &entry->sampler_object) ); + + GE( context, glSamplerParameteri (entry->sampler_object, + GL_TEXTURE_MIN_FILTER, + entry->min_filter) ); + GE( context, glSamplerParameteri (entry->sampler_object, + GL_TEXTURE_MAG_FILTER, + entry->mag_filter) ); + + set_wrap_mode (context, + entry->sampler_object, + GL_TEXTURE_WRAP_S, + entry->wrap_mode_s); + set_wrap_mode (context, + entry->sampler_object, + GL_TEXTURE_WRAP_T, + entry->wrap_mode_t); + set_wrap_mode (context, + entry->sampler_object, + GL_TEXTURE_WRAP_R, + entry->wrap_mode_p); + } + else + { + /* If sampler objects aren't supported then we'll invent a + unique number so that pipelines can still compare the + unique state just by comparing the sampler object + numbers */ + entry->sampler_object = cache->next_fake_sampler_object_number++; + } + + g_hash_table_insert (cache->hash_table_gl, entry, entry); + } + + return entry; +} + +static CoglSamplerCacheEntry * +_cogl_sampler_cache_get_entry_cogl (CoglSamplerCache *cache, + const CoglSamplerCacheEntry *key) +{ + CoglSamplerCacheEntry *entry; + + entry = g_hash_table_lookup (cache->hash_table_cogl, key); + + if (entry == NULL) + { + CoglSamplerCacheEntry canonical_key; + CoglSamplerCacheEntry *gl_entry; + + entry = g_slice_dup (CoglSamplerCacheEntry, key); + + /* Get the sampler object number from the canonical GL version + of the sampler state cache */ + canonical_key = *key; + canonicalize_key (&canonical_key); + gl_entry = _cogl_sampler_cache_get_entry_gl (cache, &canonical_key); + entry->sampler_object = gl_entry->sampler_object; + + g_hash_table_insert (cache->hash_table_cogl, entry, entry); + } + + return entry; +} + +const CoglSamplerCacheEntry * +_cogl_sampler_cache_get_default_entry (CoglSamplerCache *cache) +{ + CoglSamplerCacheEntry key; + + key.wrap_mode_s = COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC; + key.wrap_mode_t = COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC; + key.wrap_mode_p = COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC; + + key.min_filter = GL_LINEAR; + key.mag_filter = GL_LINEAR; + + return _cogl_sampler_cache_get_entry_cogl (cache, &key); +} + +const CoglSamplerCacheEntry * +_cogl_sampler_cache_update_wrap_modes (CoglSamplerCache *cache, + const CoglSamplerCacheEntry *old_entry, + CoglSamplerCacheWrapMode wrap_mode_s, + CoglSamplerCacheWrapMode wrap_mode_t, + CoglSamplerCacheWrapMode wrap_mode_p) +{ + CoglSamplerCacheEntry key = *old_entry; + + key.wrap_mode_s = wrap_mode_s; + key.wrap_mode_t = wrap_mode_t; + key.wrap_mode_p = wrap_mode_p; + + return _cogl_sampler_cache_get_entry_cogl (cache, &key); +} + +const CoglSamplerCacheEntry * +_cogl_sampler_cache_update_filters (CoglSamplerCache *cache, + const CoglSamplerCacheEntry *old_entry, + GLenum min_filter, + GLenum mag_filter) +{ + CoglSamplerCacheEntry key = *old_entry; + + key.min_filter = min_filter; + key.mag_filter = mag_filter; + + return _cogl_sampler_cache_get_entry_cogl (cache, &key); +} + +static void +hash_table_free_gl_cb (void *key, + void *value, + void *user_data) +{ + CoglContext *context = user_data; + CoglSamplerCacheEntry *entry = value; + + if ((context->private_feature_flags & + COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS)) + GE( context, glDeleteSamplers (1, &entry->sampler_object) ); + + g_slice_free (CoglSamplerCacheEntry, entry); +} + +static void +hash_table_free_cogl_cb (void *key, + void *value, + void *user_data) +{ + CoglSamplerCacheEntry *entry = value; + + g_slice_free (CoglSamplerCacheEntry, entry); +} + +void +_cogl_sampler_cache_free (CoglSamplerCache *cache) +{ + g_hash_table_foreach (cache->hash_table_gl, + hash_table_free_gl_cb, + cache->context); + + g_hash_table_destroy (cache->hash_table_gl); + + g_hash_table_foreach (cache->hash_table_cogl, + hash_table_free_cogl_cb, + cache->context); + + g_hash_table_destroy (cache->hash_table_cogl); + + g_free (cache); +} diff --git a/cogl/cogl-util.h b/cogl/cogl-util.h index d657c7654..2ad3f2666 100644 --- a/cogl/cogl-util.h +++ b/cogl/cogl-util.h @@ -96,10 +96,10 @@ _cogl_util_is_pot (unsigned int num) */ static inline unsigned int _cogl_util_one_at_a_time_hash (unsigned int hash, - void *key, + const void *key, size_t bytes) { - unsigned char *p = key; + const unsigned char *p = key; int i; for (i = 0; i < bytes; i++) diff --git a/cogl/driver/gl/cogl-gl.c b/cogl/driver/gl/cogl-gl.c index 6d390339e..e2674c4e2 100644 --- a/cogl/driver/gl/cogl-gl.c +++ b/cogl/driver/gl/cogl-gl.c @@ -453,6 +453,9 @@ _cogl_driver_update_features (CoglContext *ctx, if (_cogl_check_extension ("GL_EXT_packed_depth_stencil", gl_extensions)) private_flags |= COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL; + if (ctx->glGenSamplers) + private_flags |= COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS; + /* Cache features */ ctx->private_feature_flags |= private_flags; ctx->feature_flags |= flags; diff --git a/cogl/gl-prototypes/cogl-all-functions.h b/cogl/gl-prototypes/cogl-all-functions.h index d58b41f56..70b54d36d 100644 --- a/cogl/gl-prototypes/cogl-all-functions.h +++ b/cogl/gl-prototypes/cogl-all-functions.h @@ -210,3 +210,22 @@ COGL_EXT_FUNCTION (void, glFramebufferTexture2DMultisampleIMG, GLint level, GLsizei samples)) COGL_EXT_END () + +COGL_EXT_BEGIN (ARB_sampler_objects, 255, 255, + 0, /* not in either GLES */ + "ARB:\0", + "sampler_objects\0") +COGL_EXT_FUNCTION (void, glGenSamplers, + (GLsizei count, + GLuint *samplers)) +COGL_EXT_FUNCTION (void, glDeleteSamplers, + (GLsizei count, + const GLuint *samplers)) +COGL_EXT_FUNCTION (void, glBindSampler, + (GLuint unit, + GLuint sampler)) +COGL_EXT_FUNCTION (void, glSamplerParameteri, + (GLuint sampler, + GLenum pname, + GLint param)) +COGL_EXT_END ()