cogl-pipeline: Use the pipeline cache for the GLSL backends

The CoglPipelineCache is now extended to store templates for state
affecting vertex shaders and combined programs. The GLSL fragend,
vertend and progend now uses this to get cached shaders and a program.

When a new pipeline is created it will now get hashed three times if
the GLSL backends are in use (once for the fragend, once for the
vertend and once for the progend). Ideally we should add some way for
the progend to check its cache before the fragends and vertends are
checked so that it can bypass them entirely if it can find a cached
combined program.
This commit is contained in:
Neil Roberts 2011-06-30 18:34:05 +01:00
parent 461bff1867
commit f3b90d1717
5 changed files with 192 additions and 4 deletions

View File

@ -36,6 +36,7 @@
struct _CoglPipelineCache
{
GHashTable *fragment_hash;
GHashTable *vertex_hash;
};
static unsigned int
@ -74,6 +75,32 @@ pipeline_fragment_equal (const void *a, const void *b)
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 gboolean
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);
}
CoglPipelineCache *
cogl_pipeline_cache_new (void)
{
@ -83,6 +110,10 @@ cogl_pipeline_cache_new (void)
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);
return cache;
}
@ -91,6 +122,7 @@ void
cogl_pipeline_cache_free (CoglPipelineCache *cache)
{
g_hash_table_destroy (cache->fragment_hash);
g_hash_table_destroy (cache->vertex_hash);
g_free (cache);
}
@ -144,3 +176,61 @@ _cogl_pipeline_cache_get_fragment_template (CoglPipelineCache *cache,
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 gboolean 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)
{
unsigned int pipeline_state_for_fragment_codegen;
unsigned int pipeline_layer_state_for_fragment_codegen;
_COGL_GET_CONTEXT (ctx, NULL);
pipeline_state_for_fragment_codegen =
_cogl_pipeline_get_state_for_fragment_codegen (ctx);
pipeline_layer_state_for_fragment_codegen =
_cogl_pipeline_get_layer_state_for_fragment_codegen (ctx);
/* Currently the vertex shader state is a subset of the fragment
shader state so we can avoid a third hash table here by just
using the fragment shader table. This assert should catch it if
that ever changes */
g_assert ((pipeline_state_for_fragment_codegen |
COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN) ==
pipeline_state_for_fragment_codegen);
g_assert ((pipeline_layer_state_for_fragment_codegen |
COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN) ==
pipeline_layer_state_for_fragment_codegen);
return _cogl_pipeline_cache_get_fragment_template (cache, key_pipeline);
}

View File

@ -47,4 +47,31 @@ CoglPipeline *
_cogl_pipeline_cache_get_fragment_template (CoglPipelineCache *cache,
CoglPipeline *key_pipeline);
/*
* Gets a pipeline from the cache that has the same state as
* @key_pipeline for the state in
* COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN. If there is no
* matching pipline already then a copy of key_pipeline is stored in
* the cache so that it will be used next time the function is called
* with a similar pipeline. In that case the copy itself will be
* returned
*/
CoglPipeline *
_cogl_pipeline_cache_get_vertex_template (CoglPipelineCache *cache,
CoglPipeline *key_pipeline);
/*
* Gets a pipeline from the cache that has the same state as
* @key_pipeline for the combination of the state state in
* COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN and
* COGL_PIPELINE_STATE_AFFECTS_FRAGMENT_CODEGEN. If there is no
* matching pipline already then a copy of key_pipeline is stored in
* the cache so that it will be used next time the function is called
* with a similar pipeline. In that case the copy itself will be
* returned
*/
CoglPipeline *
_cogl_pipeline_cache_get_combined_template (CoglPipelineCache *cache,
CoglPipeline *key_pipeline);
#endif /* __COGL_PIPELINE_CACHE_H__ */

View File

@ -44,6 +44,7 @@
#include "cogl-handle.h"
#include "cogl-shader-private.h"
#include "cogl-program-private.h"
#include "cogl-pipeline-cache.h"
#include <glib.h>
@ -154,6 +155,7 @@ _cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline,
{
CoglPipelineShaderState *shader_state;
CoglPipeline *authority;
CoglPipeline *template_pipeline = NULL;
CoglProgram *user_program;
int i;
@ -197,8 +199,30 @@ _cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline,
*/
if (shader_state == NULL)
{
shader_state = shader_state_new (n_layers);
/* Check if there is already a similar cached pipeline whose
shader state we can share */
if (G_LIKELY (!(COGL_DEBUG_ENABLED
(COGL_DEBUG_DISABLE_PROGRAM_CACHES))))
{
template_pipeline =
_cogl_pipeline_cache_get_fragment_template (ctx->pipeline_cache,
authority);
shader_state = get_shader_state (template_pipeline);
}
if (shader_state)
shader_state->ref_count++;
else
shader_state = shader_state_new (n_layers);
set_shader_state (authority, shader_state);
if (template_pipeline)
{
shader_state->ref_count++;
set_shader_state (template_pipeline, shader_state);
}
}
/* If the pipeline isn't actually its own glsl-authority

View File

@ -41,6 +41,7 @@
#include "cogl-program-private.h"
#include "cogl-pipeline-fragend-glsl-private.h"
#include "cogl-pipeline-vertend-glsl-private.h"
#include "cogl-pipeline-cache.h"
#ifdef HAVE_COGL_GLES2
@ -538,6 +539,7 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline,
gboolean program_changed = FALSE;
UpdateUniformsState state;
CoglProgram *user_program;
CoglPipeline *template_pipeline = NULL;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
@ -570,9 +572,31 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline,
if (program_state == NULL)
{
program_state
= program_state_new (cogl_pipeline_get_n_layers (pipeline));
/* Check if there is already a similar cached pipeline whose
program state we can share */
if (G_LIKELY (!(COGL_DEBUG_ENABLED
(COGL_DEBUG_DISABLE_PROGRAM_CACHES))))
{
template_pipeline =
_cogl_pipeline_cache_get_combined_template (ctx->pipeline_cache,
authority);
program_state = get_program_state (template_pipeline);
}
if (program_state)
program_state->ref_count++;
else
program_state
= program_state_new (cogl_pipeline_get_n_layers (authority));
set_program_state (authority, program_state);
if (template_pipeline)
{
program_state->ref_count++;
set_program_state (template_pipeline, program_state);
}
}
if (authority != pipeline)

View File

@ -128,6 +128,7 @@ _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline,
unsigned long pipelines_difference)
{
CoglPipelineShaderState *shader_state;
CoglPipeline *template_pipeline = NULL;
CoglProgram *user_program;
_COGL_GET_CONTEXT (ctx, FALSE);
@ -164,8 +165,30 @@ _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline,
if (shader_state == NULL)
{
shader_state = shader_state_new ();
/* Check if there is already a similar cached pipeline whose
shader state we can share */
if (G_LIKELY (!(COGL_DEBUG_ENABLED
(COGL_DEBUG_DISABLE_PROGRAM_CACHES))))
{
template_pipeline =
_cogl_pipeline_cache_get_vertex_template (ctx->pipeline_cache,
authority);
shader_state = get_shader_state (template_pipeline);
}
if (shader_state)
shader_state->ref_count++;
else
shader_state = shader_state_new ();
set_shader_state (authority, shader_state);
if (template_pipeline)
{
shader_state->ref_count++;
set_shader_state (template_pipeline, shader_state);
}
}
if (authority != pipeline)