/* * Cogl * * An object oriented GL/GLES Abstraction/Utility Layer * * Copyright (C) 2011 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 * Robert Bragg */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "cogl-context-private.h" #include "cogl-pipeline-private.h" #include "cogl-pipeline-cache.h" struct _CoglPipelineCache { GHashTable *fragment_hash; GHashTable *vertex_hash; GHashTable *combined_hash; }; static unsigned int pipeline_fragment_hash (const void *data) { unsigned int fragment_state; unsigned int layer_fragment_state; _COGL_GET_CONTEXT (ctx, 0); fragment_state = _cogl_pipeline_get_state_for_fragment_codegen (ctx); layer_fragment_state = _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx); return _cogl_pipeline_hash ((CoglPipeline *)data, fragment_state, layer_fragment_state, 0); } static CoglBool pipeline_fragment_equal (const void *a, const void *b) { unsigned int fragment_state; unsigned int layer_fragment_state; _COGL_GET_CONTEXT (ctx, 0); fragment_state = _cogl_pipeline_get_state_for_fragment_codegen (ctx); layer_fragment_state = _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx); return _cogl_pipeline_equal ((CoglPipeline *)a, (CoglPipeline *)b, fragment_state, layer_fragment_state, 0); } static unsigned int pipeline_vertex_hash (const void *data) { unsigned long vertex_state = COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN; unsigned long layer_vertex_state = COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN; return _cogl_pipeline_hash ((CoglPipeline *)data, vertex_state, layer_vertex_state, 0); } static CoglBool pipeline_vertex_equal (const void *a, const void *b) { unsigned long vertex_state = COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN; unsigned long layer_vertex_state = COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN; return _cogl_pipeline_equal ((CoglPipeline *)a, (CoglPipeline *)b, vertex_state, layer_vertex_state, 0); } static unsigned int pipeline_combined_hash (const void *data) { unsigned int combined_state; unsigned int layer_combined_state; _COGL_GET_CONTEXT (ctx, 0); combined_state = _cogl_pipeline_get_state_for_fragment_codegen (ctx) | COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN; layer_combined_state = _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx) | COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN; return _cogl_pipeline_hash ((CoglPipeline *)data, combined_state, layer_combined_state, 0); } static CoglBool pipeline_combined_equal (const void *a, const void *b) { unsigned int combined_state; unsigned int layer_combined_state; _COGL_GET_CONTEXT (ctx, 0); combined_state = _cogl_pipeline_get_state_for_fragment_codegen (ctx) | COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN; layer_combined_state = _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx) | COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN; return _cogl_pipeline_equal ((CoglPipeline *)a, (CoglPipeline *)b, combined_state, layer_combined_state, 0); } CoglPipelineCache * _cogl_pipeline_cache_new (void) { CoglPipelineCache *cache = g_new (CoglPipelineCache, 1); cache->fragment_hash = g_hash_table_new_full (pipeline_fragment_hash, pipeline_fragment_equal, cogl_object_unref, cogl_object_unref); cache->vertex_hash = g_hash_table_new_full (pipeline_vertex_hash, pipeline_vertex_equal, cogl_object_unref, cogl_object_unref); cache->combined_hash = g_hash_table_new_full (pipeline_combined_hash, pipeline_combined_equal, cogl_object_unref, cogl_object_unref); return cache; } void _cogl_pipeline_cache_free (CoglPipelineCache *cache) { g_hash_table_destroy (cache->fragment_hash); g_hash_table_destroy (cache->vertex_hash); g_hash_table_destroy (cache->combined_hash); g_free (cache); } CoglPipeline * _cogl_pipeline_cache_get_fragment_template (CoglPipelineCache *cache, CoglPipeline *key_pipeline) { CoglPipeline *template = g_hash_table_lookup (cache->fragment_hash, key_pipeline); if (template == NULL) { /* XXX: I wish there was a way to insert into a GHashTable with * a pre-calculated hash value since there is a cost to * calculating the hash of a CoglPipeline and in this case we * know we have already called _cogl_pipeline_hash during the * lookup so we could pass the value through to here to avoid * hashing it again. */ /* XXX: Any keys referenced by the hash table need to remain * valid all the while that there are corresponding values, * so for now we simply make a copy of the current authority * pipeline. * * FIXME: A problem with this is that our key into the cache may * hold references to some arbitrary user textures which will * now be kept alive indefinitly which is a shame. A better * solution will be to derive a special "key pipeline" from the * authority which derives from the base Cogl pipeline (to avoid * affecting the lifetime of any other pipelines) and only takes * a copy of the state that relates to the fragment shader and * references small dummy textures instead of potentially large * user textures. */ template = cogl_pipeline_copy (key_pipeline); g_hash_table_insert (cache->fragment_hash, template, cogl_object_ref (template)); if (G_UNLIKELY (g_hash_table_size (cache->fragment_hash) > 50)) { static CoglBool seen = FALSE; if (!seen) g_warning ("Over 50 separate fragment shaders have been " "generated which is very unusual, so something " "is probably wrong!\n"); seen = TRUE; } } return template; } CoglPipeline * _cogl_pipeline_cache_get_vertex_template (CoglPipelineCache *cache, CoglPipeline *key_pipeline) { CoglPipeline *template = g_hash_table_lookup (cache->vertex_hash, key_pipeline); if (template == NULL) { template = cogl_pipeline_copy (key_pipeline); g_hash_table_insert (cache->vertex_hash, template, cogl_object_ref (template)); if (G_UNLIKELY (g_hash_table_size (cache->vertex_hash) > 50)) { static CoglBool seen = FALSE; if (!seen) g_warning ("Over 50 separate vertex shaders have been " "generated which is very unusual, so something " "is probably wrong!\n"); seen = TRUE; } } return template; } CoglPipeline * _cogl_pipeline_cache_get_combined_template (CoglPipelineCache *cache, CoglPipeline *key_pipeline) { CoglPipeline *template = g_hash_table_lookup (cache->combined_hash, key_pipeline); if (template == NULL) { template = cogl_pipeline_copy (key_pipeline); g_hash_table_insert (cache->combined_hash, template, cogl_object_ref (template)); if (G_UNLIKELY (g_hash_table_size (cache->combined_hash) > 50)) { static CoglBool seen = FALSE; if (!seen) g_warning ("Over 50 separate programs have been " "generated which is very unusual, so something " "is probably wrong!\n"); seen = TRUE; } } return template; }