mirror of
https://github.com/brl/mutter.git
synced 2024-11-22 16:10:41 -05:00
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:
parent
046434e222
commit
0087910574
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user