arbfp: Adds an ARBfp program cache

This adds a cache (A GHashTable) of ARBfp programs and before ever
starting to code-generate a new program we will always first try and
find an existing program in the cache. This uses _cogl_pipeline_hash and
_cogl_pipeline_equal to hash and compare the keys for the cache.

There is a new COGL_DEBUG=disable-program-caches option that can disable
the cache for debugging purposes.
This commit is contained in:
Robert Bragg 2010-12-03 12:01:18 +00:00
parent 046434e222
commit 0087910574
7 changed files with 118 additions and 23 deletions

View File

@ -40,6 +40,7 @@
#include <string.h>
#ifdef HAVE_COGL_GL
#include "cogl-pipeline-arbfp-private.h"
#define glActiveTexture _context->drv.pf_glActiveTexture
#endif
@ -207,6 +208,9 @@ cogl_create_context (void)
_context->legacy_depth_test_enabled = FALSE;
_context->arbfp_cache = g_hash_table_new (_cogl_pipeline_arbfp_hash,
_cogl_pipeline_arbfp_equal);
for (i = 0; i < COGL_BUFFER_BIND_TARGET_COUNT; i++)
_context->current_buffer[i] = NULL;
@ -356,6 +360,8 @@ _cogl_destroy_context (void)
g_slist_free (_context->texture_types);
g_slist_free (_context->buffer_types);
g_hash_table_unref (_context->arbfp_cache);
g_free (_context);
}

View File

@ -91,6 +91,8 @@ typedef struct
int legacy_state_set;
GHashTable *arbfp_cache;
/* Textures */
CoglHandle default_gl_texture_2d_tex;
CoglHandle default_gl_texture_rect_tex;

View File

@ -163,4 +163,9 @@ OPT (OFFSCREEN,
"offscreen",
"Trace offscreen support",
"Debug offscreen support")
OPT (DISABLE_BLENDING,
"Root Cause",
"disable-program-caches",
"Disable program caches",
"Disable fallback caches for arbfp and glsl programs")

View File

@ -32,8 +32,9 @@
#ifdef COGL_ENABLE_DEBUG
/* XXX: If you add a debug option, please also scroll down to
* cogl_arg_debug_cb() and add a "help" description of the option too.
/* XXX: If you add a debug option, please also add an option
* definition to cogl-debug-options.h. This will enable us - for
* example - to emit a "help" description for the option.
*/
/* NB: Only these options get enabled if COGL_DEBUG=all is
@ -73,7 +74,8 @@ static const GDebugKey cogl_behavioural_debug_keys[] = {
{ "disable-blending", COGL_DEBUG_DISABLE_BLENDING},
{ "disable-npot-textures", COGL_DEBUG_DISABLE_NPOT_TEXTURES},
{ "wireframe", COGL_DEBUG_WIREFRAME},
{ "disable-software-clip", COGL_DEBUG_DISABLE_SOFTWARE_CLIP}
{ "disable-software-clip", COGL_DEBUG_DISABLE_SOFTWARE_CLIP},
{ "disable-program-caches", COGL_DEBUG_DISABLE_PROGRAM_CACHES}
};
static const int n_cogl_behavioural_debug_keys =
G_N_ELEMENTS (cogl_behavioural_debug_keys);

View File

@ -59,7 +59,8 @@ typedef enum {
COGL_DEBUG_BITMAP = 1 << 26,
COGL_DEBUG_DISABLE_NPOT_TEXTURES = 1 << 27,
COGL_DEBUG_WIREFRAME = 1 << 28,
COGL_DEBUG_DISABLE_SOFTWARE_CLIP = 1 << 29
COGL_DEBUG_DISABLE_SOFTWARE_CLIP = 1 << 29,
COGL_DEBUG_DISABLE_PROGRAM_CACHES = 1 << 30
} CoglDebugFlags;
#ifdef COGL_ENABLE_DEBUG

View File

@ -32,5 +32,11 @@
extern const CoglPipelineBackend _cogl_pipeline_arbfp_backend;
unsigned int
_cogl_pipeline_arbfp_hash (const void *pipeline);
gboolean
_cogl_pipeline_arbfp_equal (const void *pipeline0, const void *pipeline1);
#endif /* __COGL_PIPELINE_ARBFP_PRIVATE_H */

View File

@ -72,6 +72,30 @@
#define GL_TEXTURE_3D 0x806F
#endif
/* When we add new pipeline or layer state groups we need to be careful to
* update backends to understand if that new state is associated with vertex,
* fragment or other processing. The idea here is to attribute which groups
* affect fragment processing and more specifically which contribute to arbfp
* code generation.
*/
#define COGL_PIPELINE_ARBFP_FRAGMENT_STATE_MASK \
(COGL_PIPELINE_STATE_LAYERS | \
COGL_PIPELINE_STATE_USER_SHADER)
#define COGL_PIPELINE_ARBFP_FRAGMENT_PROGRAM_STATE_MASK \
COGL_PIPELINE_ARBFP_FRAGMENT_STATE_MASK
#define COGL_PIPELINE_ARBFP_LAYER_FRAGMENT_STATE_MASK \
COGL_PIPELINE_LAYER_STATE_ALL
#define COGL_PIPELINE_ARBFP_LAYER_FRAGMENT_PROGRAM_STATE_MASK \
(COGL_PIPELINE_ARBFP_LAYER_FRAGMENT_STATE_MASK & \
~(COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT | \
COGL_PIPELINE_LAYER_STATE_FILTERS | \
COGL_PIPELINE_LAYER_STATE_WRAP_MODES | \
COGL_PIPELINE_LAYER_STATE_USER_MATRIX))
typedef struct _UnitState
{
int constant_id; /* The program.local[] index */
@ -239,15 +263,27 @@ _cogl_pipeline_backend_arbfp_start (CoglPipeline *pipeline,
set_arbfp_priv (authority, authority_priv);
}
/* If we don't have an existing program associated with the
* arbfp-authority then start generating code for a new program...
*/
/* If we haven't yet found an existing program then before we resort to
* generating a new arbfp program we see if we can find a suitable
* program in the arbfp_cache. */
if (!authority_priv->arbfp_program_state &&
G_LIKELY (!(cogl_debug_flags & COGL_DEBUG_DISABLE_PROGRAM_CACHES)))
{
authority_priv->arbfp_program_state =
g_hash_table_lookup (ctx->arbfp_cache, authority);
if (authority_priv->arbfp_program_state)
arbfp_program_state_ref (authority_priv->arbfp_program_state);
}
if (!authority_priv->arbfp_program_state)
{
ArbfpProgramState *arbfp_program_state =
arbfp_program_state_new (n_layers);
authority_priv->arbfp_program_state = arbfp_program_state;
/* If we don't have an existing program associated with the
* arbfp-authority then start generating code for a new program...
*/
arbfp_program_state->user_program = user_program;
if (user_program == COGL_INVALID_HANDLE)
{
@ -284,6 +320,34 @@ _cogl_pipeline_backend_arbfp_start (CoglPipeline *pipeline,
return TRUE;
}
unsigned int
_cogl_pipeline_arbfp_hash (const void *data)
{
unsigned long fragment_state =
COGL_PIPELINE_ARBFP_FRAGMENT_PROGRAM_STATE_MASK;
unsigned long layer_fragment_state =
COGL_PIPELINE_ARBFP_LAYER_FRAGMENT_PROGRAM_STATE_MASK;
CoglPipelineEvalFlags flags = COGL_PIPELINE_EVAL_FLAG_IGNORE_TEXTURE_DATA;
return _cogl_pipeline_hash ((CoglPipeline *)data,
fragment_state, layer_fragment_state,
flags);
}
gboolean
_cogl_pipeline_arbfp_equal (const void *a, const void *b)
{
unsigned long fragment_state =
COGL_PIPELINE_ARBFP_FRAGMENT_PROGRAM_STATE_MASK;
unsigned long layer_fragment_state =
COGL_PIPELINE_ARBFP_LAYER_FRAGMENT_PROGRAM_STATE_MASK;
CoglPipelineEvalFlags flags = COGL_PIPELINE_EVAL_FLAG_IGNORE_TEXTURE_DATA;
return _cogl_pipeline_equal ((CoglPipeline *)a, (CoglPipeline *)b,
fragment_state, layer_fragment_state,
flags);
}
static const char *
gl_target_to_arbfp_string (GLenum gl_target)
{
@ -842,6 +906,28 @@ _cogl_pipeline_backend_arbfp_end (CoglPipeline *pipeline,
}
arbfp_program_state->source = NULL;
if (G_LIKELY (!(cogl_debug_flags & COGL_DEBUG_DISABLE_PROGRAM_CACHES)))
{
/* 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
* _cogl_pipeline_arbfp_backend_start so we could pass the
* value through to here to avoid hashing it again.
*/
g_hash_table_insert (ctx->arbfp_cache, pipeline, arbfp_program_state);
arbfp_program_state_ref (arbfp_program_state);
if (G_UNLIKELY (g_hash_table_size (ctx->arbfp_cache) > 50))
{
static gboolean seen = FALSE;
if (!seen)
g_warning ("Over 50 separate ARBfp programs have been "
"generated which is very unusual, so something "
"is probably wrong!\n");
seen = TRUE;
}
}
}
if (arbfp_program_state->user_program != COGL_INVALID_HANDLE)
@ -918,12 +1004,7 @@ _cogl_pipeline_backend_arbfp_pipeline_pre_change_notify (
CoglPipelineState change,
const CoglColor *new_color)
{
static const unsigned long fragment_op_changes =
COGL_PIPELINE_STATE_LAYERS |
COGL_PIPELINE_STATE_USER_SHADER;
/* TODO: COGL_PIPELINE_STATE_FOG */
if (!(change & fragment_op_changes))
if (!(change & COGL_PIPELINE_ARBFP_FRAGMENT_PROGRAM_STATE_MASK))
return;
dirty_arbfp_program_state (pipeline);
@ -943,19 +1024,11 @@ _cogl_pipeline_backend_arbfp_layer_pre_change_notify (
CoglPipelineLayer *layer,
CoglPipelineLayerState change)
{
CoglPipelineBackendARBfpPrivate *priv;
static const unsigned long not_fragment_op_changes =
COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT |
COGL_PIPELINE_LAYER_STATE_TEXTURE |
COGL_PIPELINE_LAYER_STATE_FILTERS |
COGL_PIPELINE_LAYER_STATE_WRAP_MODES |
COGL_PIPELINE_LAYER_STATE_USER_MATRIX;
priv = get_arbfp_priv (owner);
CoglPipelineBackendARBfpPrivate *priv = get_arbfp_priv (owner);
if (!priv)
return;
if (!(change & not_fragment_op_changes))
if (change & COGL_PIPELINE_ARBFP_LAYER_FRAGMENT_PROGRAM_STATE_MASK)
{
dirty_arbfp_program_state (owner);
return;