CoglMaterial: Implements sparse materials design

This is a complete overhaul of the data structures used to manage
CoglMaterial state.

We have these requirements that were aiming to meet:
(Note: the references to "renderlists" correspond to the effort to
support scenegraph level shuffling of Clutter actor primitives so we can
minimize GPU state changes)

Sparse State:
We wanted a design that allows sparse descriptions of state so it scales
well as we make CoglMaterial responsible for more and more state. It
needs to scale well in terms of memory usage and the cost of operations
we need to apply to materials such as comparing, copying and flushing
their state. I.e. we would rather have these things scale by the number
of real changes a material represents not by how much overall state
CoglMaterial becomes responsible for.

Cheap Copies:
As we add support for renderlists in Clutter we will need to be able to
get an immutable handle for a given material's current state so that we
can retain a record of a primitive with its associated material without
worrying that changes to the original material will invalidate that
record.

No more flush override options:
We want to get rid of the flush overrides mechanism we currently use to
deal with texture fallbacks, wrap mode changes and to handle the use of
highlevel CoglTextures that need to be resolved into lowlevel textures
before flushing the material state.

The flush options structure has been expanding in size and the structure
is logged with every journal entry so it is not an approach that scales
well at all. It also makes flushing material state that much more
complex.

Weak Materials:
Again for renderlists we need a way to create materials derived from
other materials but without the strict requirement that modifications to
the original material wont affect the derived ("weak") material. The
only requirement is that its possible to later check if the original
material has been changed.

A summary of the new design:

A CoglMaterial now basically represents a diff against its parent.
Each material has a single parent and a mask of state that it changes.

Each group of state (such as the blending state) has an "authority"
which is found by walking up from a given material through its ancestors
checking the difference mask until a match for that group is found.

There is only one root node to the graph of all materials, which is the
default material first created when Cogl is being initialized.

All the groups of state are divided into two types, such that
infrequently changed state belongs in a separate "BigState" structure
that is only allocated and attached to a material when necessary.

CoglMaterialLayers are another sparse structure. Like CoglMaterials they
represent a diff against their parent and all the layers are part of
another graph with the "default_layer_0" layer being the root node that
Cogl creates during initialization.

Copying a material is now basically just a case of slice allocating a
CoglMaterial, setting the parent to be the source being copied and
zeroing the mask of changes.

Flush overrides should now be handled by simply relying on the cheapness
of copying a material and making changes to it. (This will be done in a
follow on commit)

Weak material support will be added in a follow on commit.
This commit is contained in:
Robert Bragg 2010-04-08 12:21:04 +01:00
parent 6c8b8cef9e
commit 1cc3ae6944
14 changed files with 5009 additions and 1935 deletions

View File

@ -374,6 +374,11 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex)
*/
_cogl_journal_flush ();
/* Notify cogl-material.c that the texture's underlying GL texture
* storage is changing so it knows it may need to bind a new texture
* if the CoglTexture is reused with the same texture unit. */
_cogl_material_texture_storage_change_notify (atlas_tex);
cogl_handle_unref (atlas_tex->sub_texture);
/* Create a new texture at the right size, not including the
@ -686,6 +691,11 @@ _cogl_atlas_texture_migrate (unsigned int n_textures,
for (i = 0; i < n_textures; i++)
{
/* Notify cogl-material.c that the texture's underlying GL texture
* storage is changing so it knows it may need to bind a new texture
* if the CoglTexture is reused with the same texture unit. */
_cogl_material_texture_storage_change_notify (textures[i].texture);
/* Skip the texture that is being added because it doesn't contain
any data yet */
if (textures[i].texture != skip_texture)

View File

@ -67,9 +67,9 @@ cogl_create_context (void)
_cogl_features_init ();
_cogl_material_init_default_material ();
_cogl_material_init_default_layers ();
_context->enable_flags = 0;
_context->color_alpha = 0;
_context->fog_enabled = FALSE;
_context->enable_backface_culling = FALSE;
@ -104,12 +104,14 @@ cogl_create_context (void)
_context->logged_vertices = g_array_new (FALSE, FALSE, sizeof (GLfloat));
_context->current_material = NULL;
_context->current_material_flags = COGL_MATERIAL_FLAGS_INIT;
_context->current_material_fallback_layers = 0;
_context->current_material_disable_layers = 0;
_context->current_material_layer0_override = 0;
_context->current_material_changes_since_flush = 0;
_context->current_material_skip_gl_color = FALSE;
_context->material0_nodes =
g_array_sized_new (FALSE, FALSE, sizeof (CoglHandle), 20);
_context->material1_nodes =
g_array_sized_new (FALSE, FALSE, sizeof (CoglHandle), 20);
_cogl_bitmask_init (&_context->texcoord_arrays_enabled);
_cogl_bitmask_init (&_context->temp_bitmask);
_cogl_bitmask_init (&_context->texcoord_arrays_to_disable);
@ -123,6 +125,8 @@ cogl_create_context (void)
_context->current_use_program_type = COGL_MATERIAL_PROGRAM_TYPE_FIXED;
_context->current_gl_program = 0;
_context->gl_blend_enable_cache = FALSE;
_context->framebuffer_stack = _cogl_create_framebuffer_stack ();
window_buffer = _cogl_onscreen_new ();
@ -173,8 +177,6 @@ cogl_create_context (void)
cogl_set_source (_context->simple_material);
_cogl_material_flush_gl_state (_context->source_material, NULL);
enable_flags =
_cogl_material_get_cogl_enable_flags (_context->source_material);
_cogl_enable (enable_flags);
_cogl_flush_face_winding ();
@ -221,6 +223,13 @@ _cogl_destroy_context (void)
if (_context->default_material)
cogl_handle_unref (_context->default_material);
if (_context->dummy_layer_dependant)
cogl_handle_unref (_context->dummy_layer_dependant);
if (_context->default_layer_n)
cogl_handle_unref (_context->default_layer_n);
if (_context->default_layer_0)
cogl_handle_unref (_context->default_layer_0);
if (_context->atlas)
_cogl_atlas_free (_context->atlas);
if (_context->atlas_texture)

View File

@ -49,10 +49,12 @@ typedef struct
gboolean features_cached;
CoglHandle default_material;
CoglHandle default_layer_0;
CoglHandle default_layer_n;
CoglHandle dummy_layer_dependant;
/* Enable cache */
unsigned long enable_flags;
guint8 color_alpha;
gboolean fog_enabled;
gboolean enable_backface_culling;
@ -91,11 +93,12 @@ typedef struct
/* Some simple caching, to minimize state changes... */
CoglHandle current_material;
unsigned long current_material_flags;
gboolean current_material_fallback_layers;
gboolean current_material_disable_layers;
GLuint current_material_layer0_override;
unsigned long current_material_changes_since_flush;
gboolean current_material_skip_gl_color;
GArray *material0_nodes;
GArray *material1_nodes;
/* Bitmask of texture coordinates arrays that are enabled */
CoglBitmask texcoord_arrays_enabled;
/* These are temporary bitmasks that are used when disabling
@ -104,6 +107,8 @@ typedef struct
CoglBitmask texcoord_arrays_to_disable;
CoglBitmask temp_bitmask;
gboolean gl_blend_enable_cache;
/* PBOs */
/* This can be used to check if a pbo is bound */
CoglBuffer *current_pbo;
@ -143,9 +148,11 @@ typedef struct
GLint max_texture_image_units;
GLint max_activateable_texture_units;
CoglHandle current_program;
/* Fragment processing programs */
CoglHandle current_program;
CoglMaterialProgramType current_use_program_type;
GLuint current_gl_program;
GLuint current_gl_program;
CoglContextDriver drv;
} CoglContext;

View File

@ -66,7 +66,8 @@ static const GDebugKey cogl_behavioural_debug_keys[] = {
{ "disable-atlas", COGL_DEBUG_DISABLE_ATLAS },
{ "disable-texturing", COGL_DEBUG_DISABLE_TEXTURING},
{ "disable-arbfp", COGL_DEBUG_DISABLE_ARBFP},
{ "disable-glsl", COGL_DEBUG_DISABLE_GLSL}
{ "disable-glsl", COGL_DEBUG_DISABLE_GLSL},
{ "disable-blending", COGL_DEBUG_DISABLE_BLENDING}
};
static const int n_cogl_behavioural_debug_keys =
G_N_ELEMENTS (cogl_behavioural_debug_keys);
@ -123,6 +124,7 @@ _cogl_parse_debug_string (const char *value,
OPT ("disable-texturing:", "disable texturing primitives");
OPT ("disable-arbfp:", "disable use of ARBfp");
OPT ("disable-glsl:", "disable use of GLSL");
OPT ("disable-blending:", "disable use of blending");
OPT ("show-source:", "show generated ARBfp/GLSL");
OPT ("opengl:", "traces some select OpenGL calls");
OPT ("offscreen:", "debug offscreen support");

View File

@ -50,7 +50,8 @@ typedef enum {
COGL_DEBUG_DISABLE_TEXTURING = 1 << 19,
COGL_DEBUG_DISABLE_ARBFP = 1 << 20,
COGL_DEBUG_DISABLE_GLSL = 1 << 21,
COGL_DEBUG_SHOW_SOURCE = 1 << 22
COGL_DEBUG_SHOW_SOURCE = 1 << 22,
COGL_DEBUG_DISABLE_BLENDING = 1 << 23
} CoglDebugFlags;
#ifdef COGL_ENABLE_DEBUG

View File

@ -97,11 +97,10 @@ cogl_gl_error_to_string (GLenum error_code);
#endif /* COGL_GL_DEBUG */
#define COGL_ENABLE_BLEND (1<<1)
#define COGL_ENABLE_ALPHA_TEST (1<<2)
#define COGL_ENABLE_VERTEX_ARRAY (1<<3)
#define COGL_ENABLE_COLOR_ARRAY (1<<4)
#define COGL_ENABLE_BACKFACE_CULLING (1<<5)
#define COGL_ENABLE_ALPHA_TEST (1<<1)
#define COGL_ENABLE_VERTEX_ARRAY (1<<2)
#define COGL_ENABLE_COLOR_ARRAY (1<<3)
#define COGL_ENABLE_BACKFACE_CULLING (1<<4)
void
_cogl_features_init (void);

View File

@ -318,10 +318,6 @@ _cogl_journal_flush_material_and_entries (CoglJournalEntry *batch_start,
_cogl_material_flush_gl_state (batch_start->material,
&batch_start->flush_options);
/* FIXME: This api is a bit yukky, ideally it will be removed if we
* re-work the _cogl_enable mechanism */
enable_flags |= _cogl_material_get_cogl_enable_flags (batch_start->material);
if (ctx->enable_backface_culling)
enable_flags |= COGL_ENABLE_BACKFACE_CULLING;
@ -356,7 +352,8 @@ compare_entry_materials (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
if (_cogl_material_equal (entry0->material,
&entry0->flush_options,
entry1->material,
&entry1->flush_options))
&entry1->flush_options,
TRUE))
return TRUE;
else
return FALSE;

View File

@ -3,7 +3,7 @@
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2008,2009 Intel Corporation.
* Copyright (C) 2008,2009,2010 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,7 +16,8 @@
* 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 <http://www.gnu.org/licenses/>.
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
*
*
@ -62,8 +63,9 @@ typedef struct _CoglTextureUnit
/* Whether or not the corresponding gl_target has been glEnabled */
gboolean enabled;
/* The GL target currently glEnabled or 0 if .enabled == FALSE */
GLenum enabled_gl_target;
/* The GL target currently glEnabled or the target last enabled
* if .enabled == FALSE */
GLenum current_gl_target;
/* The raw GL texture object name for which we called glBindTexture when
* we flushed the last layer. (NB: The CoglTexture associated
@ -80,13 +82,13 @@ typedef struct _CoglTextureUnit
/* We have many components in Cogl that need to temporarily bind arbitrary
* textures e.g. to query texture object parameters and since we don't
* want that to result in too much redundant reflushing of layer state
* when all that's needed is to re-bind the layers gl_texture we use this
* to track when the .layer_gl_texture state is invalid.
* when all that's needed is to re-bind the layer's gl_texture we use this
* to track when the unit->gl_texture state is out of sync with the GL
* texture object really bound too (GL_TEXTURE0+unit->index).
*
* XXX: as a further optimization cogl-material.c uses a convention
* of always leaving texture unit 1 active when not dealing with the
* flushing of layer state, so we can assume this is only ever TRUE
* for unit 1.
* of always using texture unit 1 for these transient bindings so we
* can assume this is only ever TRUE for unit 1.
*/
gboolean dirty_gl_texture;
@ -116,24 +118,16 @@ typedef struct _CoglTextureUnit
* invalidated though these flags can be used to optimize the state
* flush of the next layer
*/
unsigned long layer_differences;
unsigned long layer_changes_since_flush;
/* The options that may have affected how the layer state updated
* this texture unit. */
gboolean fallback;
gboolean layer0_overridden;
/* When flushing a layers state, fallback options may mean that a
* different CoglTexture is used than layer->texture.
*
* Once a layers state has been flushed we have to keep track of
* changes to that layer so if we are asked to re-flush the same
* layer later we will know what work is required. This also means
* we need to keep track of changes to the CoglTexture of that layer
* so we need to explicitly keep a reference to the final texture
* chosen.
*/
CoglHandle texture;
/* Whenever a CoglTexture's internal GL texture storage changes
* cogl-material.c is notified with a call to
* _cogl_material_texture_storage_change_notify which inturn sets
* this to TRUE for each texture unit that it is currently bound
* too. When we later come to flush some material state then we will
* always check this to potentially force an update of the texture
* state even if the material hasn't changed. */
gboolean texture_storage_changed;
} CoglTextureUnit;
@ -148,63 +142,50 @@ _cogl_bind_gl_texture_transient (GLenum gl_target,
GLuint gl_texture,
gboolean is_foreign);
typedef enum _CoglMaterialEqualFlags
#if defined (HAVE_COGL_GL)
/* glsl, arbfp, fixed */
#define COGL_MATERIAL_N_BACKENDS 3
#elif defined (HAVE_COGL_GLES2)
/* glsl, fixed */
#define COGL_MATERIAL_N_BACKENDS 2
#else /* HAVE_COGL_GLES */
/* fixed */
#define COGL_MATERIAL_N_BACKENDS 1
#endif
typedef enum
{
/* Return FALSE if any component of either material isn't set to its
* default value. (Note: if the materials have corresponding flush
* options indicating that e.g. the material color won't be flushed then
* this will not assert a default color value.) */
COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS = 1L<<0,
COGL_MATERIAL_LAYER_STATE_UNIT = 1L<<0,
COGL_MATERIAL_LAYER_STATE_TEXTURE = 1L<<1,
COGL_MATERIAL_LAYER_STATE_FILTERS = 1L<<2,
COGL_MATERIAL_LAYER_STATE_WRAP_MODES = 1L<<3,
} CoglMaterialEqualFlags;
COGL_MATERIAL_LAYER_STATE_COMBINE = 1L<<4,
COGL_MATERIAL_LAYER_STATE_COMBINE_CONSTANT = 1L<<5,
COGL_MATERIAL_LAYER_STATE_USER_MATRIX = 1L<<6,
typedef enum _CoglMaterialLayerDifferenceFlags
/* COGL_MATERIAL_LAYER_STATE_TEXTURE_INTERN = 1L<<7, */
COGL_MATERIAL_LAYER_STATE_ALL_SPARSE =
COGL_MATERIAL_LAYER_STATE_UNIT |
COGL_MATERIAL_LAYER_STATE_TEXTURE |
COGL_MATERIAL_LAYER_STATE_FILTERS |
COGL_MATERIAL_LAYER_STATE_WRAP_MODES |
COGL_MATERIAL_LAYER_STATE_COMBINE |
COGL_MATERIAL_LAYER_STATE_COMBINE_CONSTANT |
COGL_MATERIAL_LAYER_STATE_USER_MATRIX,
COGL_MATERIAL_LAYER_STATE_NEEDS_BIG_STATE =
COGL_MATERIAL_LAYER_STATE_COMBINE |
COGL_MATERIAL_LAYER_STATE_COMBINE_CONSTANT |
COGL_MATERIAL_LAYER_STATE_USER_MATRIX
} CoglMaterialLayerState;
typedef struct
{
COGL_MATERIAL_LAYER_DIFFERENCE_TEXTURE = 1L<<0,
COGL_MATERIAL_LAYER_DIFFERENCE_COMBINE = 1L<<1,
COGL_MATERIAL_LAYER_DIFFERENCE_COMBINE_CONSTANT = 1L<<2,
COGL_MATERIAL_LAYER_DIFFERENCE_USER_MATRIX = 1L<<3,
COGL_MATERIAL_LAYER_DIFFERENCE_FILTERS = 1L<<4
} CoglMaterialLayerDifferenceFlags;
typedef enum _CoglMaterialLayerChangeFlags
{
COGL_MATERIAL_LAYER_CHANGE_TEXTURE = 1L<<0,
COGL_MATERIAL_LAYER_CHANGE_COMBINE = 1L<<1,
COGL_MATERIAL_LAYER_CHANGE_COMBINE_CONSTANT = 1L<<2,
COGL_MATERIAL_LAYER_CHANGE_USER_MATRIX = 1L<<3,
COGL_MATERIAL_LAYER_CHANGE_FILTERS = 1L<<4,
COGL_MATERIAL_LAYER_CHANGE_TEXTURE_INTERN = 1L<<5,
COGL_MATERIAL_LAYER_CHANGE_UNIT = 1L<<6
} CoglMaterialLayerChangeFlags;
struct _CoglMaterialLayer
{
CoglHandleObject _parent;
/* Parent material */
CoglMaterial *material;
unsigned int index; /*!< lowest index is blended first then others on
top */
int unit_index;
unsigned long differences;
CoglHandle texture; /*!< The texture for this layer, or
COGL_INVALID_HANDLE for an empty layer */
CoglMaterialFilter mag_filter;
CoglMaterialFilter min_filter;
CoglMaterialWrapMode wrap_mode_s;
CoglMaterialWrapMode wrap_mode_t;
CoglMaterialWrapMode wrap_mode_r;
/* Determines how the color of individual texture fragments
* are calculated. */
/* The texture combine state determines how the color of individual
* texture fragments are calculated. */
GLint texture_combine_rgb_func;
GLint texture_combine_rgb_src[3];
GLint texture_combine_rgb_op[3];
@ -213,104 +194,363 @@ struct _CoglMaterialLayer
GLint texture_combine_alpha_src[3];
GLint texture_combine_alpha_op[3];
GLfloat texture_combine_constant[4];
/* TODO: Support purely GLSL based material layers */
float texture_combine_constant[4];
/* The texture matrix dscribes how to transform texture coordinates */
CoglMatrix matrix;
} CoglMaterialLayerBigState;
struct _CoglMaterialLayer
{
/* XXX: Please think twice about adding members that *have* be
* initialized during a _cogl_material_layer_copy. We are aiming
* to have copies be as cheap as possible and copies may be
* done by the primitives APIs which means they may happen
* in performance critical code paths.
*
* XXX: If you are extending the state we track please consider if
* the state is expected to vary frequently across many materials or
* if the state can be shared among many derived materials instead.
* This will determine if the state should be added directly to this
* structure which will increase the memory overhead for *all*
* layers or if instead it can go under ->big_state.
*/
/* the parent in terms of class hierarchy */
CoglHandleObject _parent;
/* Some layers have a material owner, which is to say that the layer
* is referenced in that materials->layer_differences list. A layer
* doesn't always have an owner and may simply be an ancestor for
* other layers that keeps track of some shared state. */
CoglMaterial *owner;
/* Layers are sparse structures defined as a diff against
* their parent... */
CoglMaterialLayer *parent;
/* As an optimization for creating leaf node layers (the most
* common) we don't require any list node allocations to link
* to a single descendant. */
CoglMaterialLayer *first_child;
/* Layers are sparse structures defined as a diff against
* their parent and may have multiple children which depend
* on them to define the values of properties which they don't
* change. */
GList *children;
/* The lowest index is blended first then others on top */
int index;
/* Different material backends (GLSL/ARBfp/Fixed Function) may
* want to associate private data with a layer... */
void *backend_priv;
* want to associate private data with a layer...
*
* NB: we have per backend pointers because a layer may be
* associated with multiple materials with different backends.
*/
void *backend_priv[COGL_MATERIAL_N_BACKENDS];
/* A mask of which state groups are different in this layer
* in comparison to its parent. */
unsigned long differences;
/* Common differences
*
* As a basic way to reduce memory usage we divide the layer
* state into two groups; the minimal state modified in 90% of
* all layers and the rest, so that the second group can
* be allocated dynamically when required.
*/
/* Each layer is directly associated with a single texture unit */
int unit_index;
/* The texture for this layer, or COGL_INVALID_HANDLE for an empty
* layer */
CoglHandle texture;
gboolean texture_overridden;
/* If ->texture_overridden == TRUE then the texture is instead
* defined by these... */
GLuint slice_gl_texture;
GLenum slice_gl_target;
CoglMaterialFilter mag_filter;
CoglMaterialFilter min_filter;
CoglMaterialWrapMode wrap_mode_s;
CoglMaterialWrapMode wrap_mode_t;
CoglMaterialWrapMode wrap_mode_r;
/* Infrequent differences aren't currently tracked in
* a separate, dynamically allocated structure as they are
* for materials... */
CoglMaterialLayerBigState *big_state;
/* bitfields */
/* Determines if layer->first_child and layer->children are
* initialized pointers. */
unsigned int has_children:1;
/* Determines if layer->big_state is valid */
unsigned int has_big_state:1;
};
typedef enum _CoglMaterialFlags
/* Used in material->differences masks and for notifying material
* state changes... */
typedef enum _CoglMaterialState
{
COGL_MATERIAL_FLAG_DEFAULT_COLOR = 1L<<1,
COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL = 1L<<2,
COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC = 1L<<3,
COGL_MATERIAL_FLAG_ENABLE_BLEND = 1L<<4,
COGL_MATERIAL_FLAG_DEFAULT_BLEND = 1L<<5,
COGL_MATERIAL_FLAG_DEFAULT_USER_SHADER = 1L<<6,
COGL_MATERIAL_FLAG_DEFAULT_LAYERS = 1L<<7
} CoglMaterialFlags;
COGL_MATERIAL_STATE_COLOR = 1L<<0,
COGL_MATERIAL_STATE_BLEND_ENABLE = 1L<<1,
COGL_MATERIAL_STATE_LAYERS = 1L<<2,
/* This defines the initialization state for
* ctx->current_material_flags which should result in the first
* material flush explicitly initializing everything
*/
#define COGL_MATERIAL_FLAGS_INIT \
COGL_MATERIAL_FLAG_DEFAULT_USER_SHADER
COGL_MATERIAL_STATE_LIGHTING = 1L<<3,
COGL_MATERIAL_STATE_ALPHA_FUNC = 1L<<4,
COGL_MATERIAL_STATE_BLEND = 1L<<5,
COGL_MATERIAL_STATE_USER_SHADER = 1L<<6,
typedef enum _CoglMaterialChangeFlag
COGL_MATERIAL_STATE_REAL_BLEND_ENABLE = 1L<<7,
COGL_MATERIAL_STATE_ALL_SPARSE =
COGL_MATERIAL_STATE_COLOR |
COGL_MATERIAL_STATE_BLEND_ENABLE |
COGL_MATERIAL_STATE_LAYERS |
COGL_MATERIAL_STATE_LIGHTING |
COGL_MATERIAL_STATE_ALPHA_FUNC |
COGL_MATERIAL_STATE_BLEND |
COGL_MATERIAL_STATE_USER_SHADER,
COGL_MATERIAL_STATE_AFFECTS_BLENDING =
COGL_MATERIAL_STATE_COLOR |
COGL_MATERIAL_STATE_BLEND_ENABLE |
COGL_MATERIAL_STATE_LAYERS |
COGL_MATERIAL_STATE_LIGHTING |
COGL_MATERIAL_STATE_BLEND |
COGL_MATERIAL_STATE_USER_SHADER,
COGL_MATERIAL_STATE_NEEDS_BIG_STATE =
COGL_MATERIAL_STATE_LIGHTING |
COGL_MATERIAL_STATE_ALPHA_FUNC |
COGL_MATERIAL_STATE_BLEND |
COGL_MATERIAL_STATE_USER_SHADER
} CoglMaterialState;
typedef enum
{
COGL_MATERIAL_CHANGE_COLOR = 1L<<1,
COGL_MATERIAL_CHANGE_GL_MATERIAL = 1L<<2,
COGL_MATERIAL_CHANGE_ALPHA_FUNC = 1L<<3,
COGL_MATERIAL_CHANGE_ENABLE_BLEND = 1L<<4,
COGL_MATERIAL_CHANGE_BLEND = 1L<<5,
COGL_MATERIAL_CHANGE_USER_SHADER = 1L<<6,
COGL_MATERIAL_CHANGE_LAYERS = 1L<<7
} CoglMaterialChangeFlag;
COGL_MATERIAL_LIGHTING_STATE_PROPERTY_AMBIENT = 1,
COGL_MATERIAL_LIGHTING_STATE_PROPERTY_DIFFUSE,
COGL_MATERIAL_LIGHTING_STATE_PROPERTY_SPECULAR,
COGL_MATERIAL_LIGHTING_STATE_PROPERTY_EMISSION,
COGL_MATERIAL_LIGHTING_STATE_PROPERTY_SHININESS
} CoglMaterialLightingStateProperty;
struct _CoglMaterial
typedef struct
{
CoglHandleObject _parent;
unsigned long journal_ref_count;
int backend;
unsigned long flags;
/* If no lighting is enabled; this is the basic material color */
GLubyte unlit[4];
/* Standard OpenGL lighting model attributes */
GLfloat ambient[4];
GLfloat diffuse[4];
GLfloat specular[4];
GLfloat emission[4];
GLfloat shininess;
float ambient[4];
float diffuse[4];
float specular[4];
float emission[4];
float shininess;
} CoglMaterialLightingState;
typedef struct
{
/* Determines what fragments are discarded based on their alpha */
CoglMaterialAlphaFunc alpha_func;
GLfloat alpha_func_reference;
} CoglMaterialAlphaFuncState;
typedef enum _CoglMaterialBlendEnable
{
/* XXX: we want to detect users mistakenly using TRUE or FALSE
* so start the enum at 2. */
COGL_MATERIAL_BLEND_ENABLE_ENABLED = 2,
COGL_MATERIAL_BLEND_ENABLE_DISABLED,
COGL_MATERIAL_BLEND_ENABLE_AUTOMATIC
} CoglMaterialBlendEnable;
typedef struct
{
/* Determines how this material is blended with other primitives */
#ifndef HAVE_COGL_GLES
GLenum blend_equation_rgb;
GLenum blend_equation_alpha;
GLint blend_src_factor_alpha;
GLint blend_dst_factor_alpha;
GLfloat blend_constant[4];
GLenum blend_equation_rgb;
GLenum blend_equation_alpha;
GLint blend_src_factor_alpha;
GLint blend_dst_factor_alpha;
CoglColor blend_constant;
#endif
GLint blend_src_factor_rgb;
GLint blend_dst_factor_rgb;
GLint blend_src_factor_rgb;
GLint blend_dst_factor_rgb;
} CoglMaterialBlendState;
typedef struct
{
CoglMaterialLightingState lighting_state;
CoglMaterialAlphaFuncState alpha_state;
CoglMaterialBlendState blend_state;
CoglHandle user_program;
} CoglMaterialBigState;
GList *layers;
unsigned int n_layers;
typedef enum
{
COGL_MATERIAL_FLAG_DIRTY_LAYERS_CACHE = 1L<<0,
COGL_MATERIAL_FLAG_DIRTY_GET_LAYERS_LIST = 1L<<1
} CoglMaterialFlag;
void *backend_priv;
typedef struct
{
CoglMaterial *owner;
CoglMaterialLayer *layer;
} CoglMaterialLayerCacheEntry;
struct _CoglMaterial
{
/* XXX: Please think twice about adding members that *have* be
* initialized during a cogl_material_copy. We are aiming to have
* copies be as cheap as possible and copies may be done by the
* primitives APIs which means they may happen in performance
* critical code paths.
*
* XXX: If you are extending the state we track please consider if
* the state is expected to vary frequently across many materials or
* if the state can be shared among many derived materials instead.
* This will determine if the state should be added directly to this
* structure which will increase the memory overhead for *all*
* materials or if instead it can go under ->big_state.
*/
/* the parent in terms of class hierarchy */
CoglHandleObject _parent;
/* We need to track if a material is referenced in the journal
* because we can't allow modification to these materials without
* flushing the journal first */
unsigned long journal_ref_count;
/* Materials are sparse structures defined as a diff against
* their parent. */
CoglMaterial *parent;
/* As an optimization for creating leaf node materials (the most
* common) we don't require any list node allocations to link
* to a single descendant.
*
* Only valid if ->has_children bitfield is set */
CoglMaterial *first_child;
/* Materials are sparse structures defined as a diff against
* their parent and may have multiple children which depend
* on them to define the values of properties which they don't
* change.
*
* Only valid if ->has_children bitfield is set */
GList *children;
/* A mask of which sparse state groups are different in this
* material in comparison to its parent. */
unsigned long differences;
/* The fragment processing backend identified by the ->backend
* bitfield can associate private data with a material. */
void *backend_priv;
/* This is the primary color of the material.
*
* This is a sparse property, ref COGL_MATERIAL_STATE_COLOR */
CoglColor color;
/* A material may be made up with multiple layers used to combine
* textures together.
*
* This is sparse state, ref COGL_MATERIAL_STATE_LAYERS */
GList *layer_differences;
unsigned int n_layers;
/* As a basic way to reduce memory usage we divide the material
* state into two groups; the minimal state modified in 90% of
* all materials and the rest, so that the second group can
* be allocated dynamically when required... */
CoglMaterialBigState *big_state;
/* Cached state... */
/* A cached, complete list of the layers this material depends
* on sorted by layer->unit_index. */
CoglMaterialLayer **layers_cache;
/* To avoid a separate ->layers_cache allocation for common
* materials with only a few layers... */
CoglMaterialLayer *short_layers_cache[3];
/* The deprecated cogl_material_get_layers() API returns a
* const GList of layers, which we track here... */
GList *deprecated_get_layers_list;
/* XXX: consider adding an authorities cache to speed up sparse
* property value lookups:
* CoglMaterial *authorities_cache[COGL_MATERIAL_N_SPARSE_PROPERTIES];
* and corresponding authorities_cache_dirty:1 bitfield
*/
/* bitfields */
/* Determines if material->big_state is valid */
unsigned int has_big_state:1;
/* By default blending is enabled automatically depending on the
* unlit color, the lighting colors or the texture format. The user
* can override this to explicitly enable or disable blending.
*
* This is a sparse property */
unsigned int blend_enable:3;
/* There are many factors that can determine if we need to enable
* blending, this holds our final decision */
unsigned int real_blend_enable:1;
/* Determines if material->first_child and material->children are
* initialized pointers. */
unsigned int has_children:1;
unsigned int layers_cache_dirty:1;
unsigned int deprecated_get_layers_list_dirty:1;
/* There are multiple fragment processing backends for CoglMaterial,
* glsl, arbfp and fixed. This identifies the backend being used for
* the material and any private state the backend has associated
* with the material. */
unsigned int backend:3;
/* Determines if ->backend_priv has been initialized */
unsigned int backend_priv_set:1;
};
typedef struct _CoglMaterialBackend
{
int (*get_max_texture_units) (void);
gboolean (*start) (CoglMaterial *material);
gboolean (*add_layer) (CoglMaterialLayer *layer);
gboolean (*start) (CoglMaterial *material,
int n_layers,
unsigned long materials_difference);
gboolean (*add_layer) (CoglMaterial *material,
CoglMaterialLayer *layer,
unsigned long layers_difference);
gboolean (*passthrough) (CoglMaterial *material);
gboolean (*end) (CoglMaterial *material);
gboolean (*end) (CoglMaterial *material,
unsigned long materials_difference);
void (*material_change_notify) (CoglMaterial *material,
unsigned long changes,
GLubyte *new_color);
void (*layer_change_notify) (CoglMaterialLayer *layer,
unsigned long changes);
void (*material_pre_change_notify) (CoglMaterial *material,
CoglMaterialState change,
const CoglColor *new_color);
void (*layer_pre_change_notify) (CoglMaterialLayer *layer,
CoglMaterialLayerState change);
void (*free_priv) (CoglMaterial *material);
void (*free_layer_priv) (CoglMaterialLayer *layer);
} CoglMaterialBackend;
typedef enum
@ -320,6 +560,12 @@ typedef enum
COGL_MATERIAL_PROGRAM_TYPE_FIXED
} CoglMaterialProgramType;
void
_cogl_material_init_default_material (void);
void
_cogl_material_init_default_layers (void);
/*
* SECTION:cogl-material-internals
* @short_description: Functions for creating custom primitives that make use
@ -331,31 +577,8 @@ typedef enum
* able to fill your geometry according to a given Cogl material.
*/
/*
* _cogl_material_init_default_material:
*
* This initializes the first material owned by the Cogl context. All
* subsequently instantiated materials created via the cogl_material_new()
* API will initially be a copy of this material.
*/
void
_cogl_material_init_default_material (void);
/*
* cogl_material_get_cogl_enable_flags:
* @material: A CoglMaterial object
*
* This determines what flags need to be passed to cogl_enable before this
* material can be used. Normally you shouldn't need to use this function
* directly since Cogl will do this internally, but if you are developing
* custom primitives directly with OpenGL you may want to use this.
*
* Note: This API is hopfully just a stop-gap solution. Ideally cogl_enable
* will be replaced.
*/
/* TODO: find a nicer solution! */
unsigned long
_cogl_material_get_cogl_enable_flags (CoglHandle handle);
gboolean
_cogl_material_get_real_blend_enabled (CoglHandle handle);
gboolean
_cogl_material_layer_has_user_matrix (CoglHandle layer_handle);
@ -397,11 +620,35 @@ typedef enum _CoglMaterialFlushFlag
COGL_MATERIAL_FLUSH_WRAP_MODE_OVERRIDES = 1L<<4
} CoglMaterialFlushFlag;
/* These constants are used to fill in wrap_mode_overrides */
#define COGL_MATERIAL_WRAP_MODE_OVERRIDE_NONE 0 /* no override */
#define COGL_MATERIAL_WRAP_MODE_OVERRIDE_REPEAT 1
#define COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE 2
#define COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_BORDER 3
/* This isn't defined in the GLES headers */
#ifndef GL_CLAMP_TO_BORDER
#define GL_CLAMP_TO_BORDER 0x812d
#endif
/* 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 CoglMaterialWrapMode enum
* so no conversion is actually needed.
*/
typedef enum _CoglMaterialWrapModeInternal
{
COGL_MATERIAL_WRAP_MODE_INTERNAL_REPEAT = GL_REPEAT,
COGL_MATERIAL_WRAP_MODE_INTERNAL_CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE,
COGL_MATERIAL_WRAP_MODE_INTERNAL_CLAMP_TO_BORDER = GL_CLAMP_TO_BORDER,
COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC = GL_ALWAYS
} CoglMaterialWrapModeInternal;
typedef enum _CoglMaterialWrapModeOverride
{
COGL_MATERIAL_WRAP_MODE_OVERRIDE_NONE = 0,
COGL_MATERIAL_WRAP_MODE_OVERRIDE_REPEAT =
COGL_MATERIAL_WRAP_MODE_INTERNAL_REPEAT,
COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE =
COGL_MATERIAL_WRAP_MODE_INTERNAL_CLAMP_TO_EDGE,
COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_BORDER =
COGL_MATERIAL_WRAP_MODE_INTERNAL_CLAMP_TO_BORDER,
} CoglMaterialWrapModeOverride;
/* There can't be more than 32 layers because we need to fit a bitmask
of the layers into a guint32 */
@ -411,9 +658,9 @@ typedef struct _CoglMaterialWrapModeOverrides
{
struct
{
unsigned long s : 2;
unsigned long t : 2;
unsigned long r : 2;
CoglMaterialWrapModeOverride s;
CoglMaterialWrapModeOverride t;
CoglMaterialWrapModeOverride r;
} values[COGL_MATERIAL_MAX_LAYERS];
} CoglMaterialWrapModeOverrides;
@ -443,7 +690,8 @@ gboolean
_cogl_material_equal (CoglHandle material0_handle,
CoglMaterialFlushOptions *material0_flush_options,
CoglHandle material1_handle,
CoglMaterialFlushOptions *material1_flush_options);
CoglMaterialFlushOptions *material1_flush_options,
gboolean skip_gl_color);
CoglHandle
_cogl_material_journal_ref (CoglHandle material_handle);
@ -468,11 +716,21 @@ _cogl_material_set_user_program (CoglHandle handle,
void
_cogl_delete_gl_texture (GLuint gl_texture);
void
_cogl_material_texture_storage_change_notify (CoglHandle texture);
void
_cogl_material_apply_legacy_state (CoglHandle handle);
void
_cogl_gl_use_program_wrapper (GLuint program);
CoglMaterialBlendEnable
_cogl_material_get_blend_enabled (CoglHandle handle);
void
_cogl_material_set_blend_enabled (CoglHandle handle,
CoglMaterialBlendEnable enable);
#endif /* __COGL_MATERIAL_PRIVATE_H */

File diff suppressed because it is too large Load Diff

View File

@ -106,7 +106,11 @@ typedef enum {
* Since: 1.4
*/
/* GL_ALWAYS is just used here as a value that is known not to clash
with any valid GL wrap modes */
* with any valid GL wrap modes
*
* XXX: keep the values in sync with the CoglMaterialWrapModeInternal
* enum so no conversion is actually needed.
*/
typedef enum {
COGL_MATERIAL_WRAP_MODE_REPEAT = GL_REPEAT,
COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE,

View File

@ -145,7 +145,6 @@ _cogl_path_stroke_nodes (void)
* always be done first when preparing to draw. */
_cogl_framebuffer_flush_state (_cogl_get_framebuffer (), 0);
enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material);
_cogl_enable (enable_flags);
options.flags = COGL_MATERIAL_FLUSH_DISABLE_MASK;
@ -234,8 +233,6 @@ _cogl_add_path_to_stencil_buffer (CoglPath *path,
_cogl_material_flush_gl_state (ctx->source_material, NULL);
enable_flags |=
_cogl_material_get_cogl_enable_flags (ctx->source_material);
_cogl_enable (enable_flags);
GE( glEnable (GL_STENCIL_TEST) );
@ -394,8 +391,8 @@ _cogl_path_fill_nodes_scanlines (CoglPathNode *path,
_cogl_material_flush_gl_state (ctx->source_material, NULL);
_cogl_enable (COGL_ENABLE_VERTEX_ARRAY
| (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0));
_cogl_enable (COGL_ENABLE_VERTEX_ARRAY);
/* clear scanline intersection lists */
for (i = 0; i < bounds_h; i++)

View File

@ -1073,6 +1073,8 @@ cogl_polygon (const CoglTextureVertex *vertices,
GLfloat *v;
CoglMaterialWrapModeOverrides wrap_mode_overrides;
CoglMaterialWrapModeOverrides *wrap_mode_overrides_p = NULL;
CoglHandle original_source_material;
gboolean overrode_material = FALSE;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
@ -1206,18 +1208,31 @@ cogl_polygon (const CoglTextureVertex *vertices,
/* Prepare GL state */
enable_flags = COGL_ENABLE_VERTEX_ARRAY;
enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material);
if (ctx->enable_backface_culling)
enable_flags |= COGL_ENABLE_BACKFACE_CULLING;
if (use_color)
{
enable_flags |= COGL_ENABLE_COLOR_ARRAY | COGL_ENABLE_BLEND;
CoglHandle override;
enable_flags |= COGL_ENABLE_COLOR_ARRAY;
GE( glColorPointer (4, GL_UNSIGNED_BYTE,
stride_bytes,
/* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */
v + 3 + 2 * n_layers) );
if (!_cogl_material_get_real_blend_enabled (ctx->source_material))
{
CoglMaterialBlendEnable blend_enabled =
COGL_MATERIAL_BLEND_ENABLE_ENABLED;
original_source_material = ctx->source_material;
override = cogl_material_copy (original_source_material);
_cogl_material_set_blend_enabled (override, blend_enabled);
/* XXX: cogl_push_source () */
overrode_material = TRUE;
ctx->source_material = override;
}
}
_cogl_enable (enable_flags);
@ -1253,6 +1268,16 @@ cogl_polygon (const CoglTextureVertex *vertices,
fallback_layers,
wrap_mode_overrides_p);
/* XXX: cogl_pop_source () */
if (overrode_material)
{
cogl_handle_unref (ctx->source_material);
ctx->source_material = original_source_material;
/* XXX: when we have weak materials then any override material
* should get associated with the original material so we don't
* create lots of one-shot materials! */
}
/* Reset the size of the logged vertex array because rendering
rectangles expects it to start at 0 */
g_array_set_size (ctx->logged_vertices, 0);

View File

@ -1525,6 +1525,8 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
_COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
source = ctx->source_material;
if (buffer->new_attributes)
cogl_vertex_buffer_submit_real (buffer);
@ -1572,13 +1574,21 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
switch (type)
{
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_COLOR_ARRAY:
enable_flags |= COGL_ENABLE_COLOR_ARRAY | COGL_ENABLE_BLEND;
enable_flags |= COGL_ENABLE_COLOR_ARRAY;
/* GE (glEnableClientState (GL_COLOR_ARRAY)); */
pointer = (const GLvoid *)(base + attribute->u.vbo_offset);
GE (glColorPointer (attribute->n_components,
gl_type,
attribute->stride,
pointer));
if (!_cogl_material_get_real_blend_enabled (ctx->source_material))
{
CoglMaterialBlendEnable blend_enable =
COGL_MATERIAL_BLEND_ENABLE_ENABLED;
source = cogl_material_copy (ctx->source_material);
_cogl_material_set_blend_enabled (source, blend_enable);
}
break;
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_NORMAL_ARRAY:
/* FIXME: go through cogl cache to enable normal array */
@ -1634,7 +1644,7 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
}
}
layers = cogl_material_get_layers (ctx->source_material);
layers = cogl_material_get_layers (source);
for (tmp = (GList *)layers, i = 0;
tmp != NULL;
tmp = tmp->next, i++)
@ -1719,14 +1729,13 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
if (G_UNLIKELY (ctx->legacy_state_set))
{
source = cogl_material_copy (ctx->source_material);
/* If we haven't already created a derived material... */
if (source == ctx->source_material)
source = cogl_material_copy (ctx->source_material);
_cogl_material_apply_legacy_state (source);
}
else
source = ctx->source_material;
_cogl_material_flush_gl_state (source, &options);
enable_flags |= _cogl_material_get_cogl_enable_flags (source);
if (ctx->enable_backface_culling)
enable_flags |= COGL_ENABLE_BACKFACE_CULLING;

View File

@ -266,10 +266,6 @@ _cogl_enable (unsigned long flags)
*/
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
toggle_flag (ctx, flags,
COGL_ENABLE_BLEND,
GL_BLEND);
toggle_flag (ctx, flags,
COGL_ENABLE_BACKFACE_CULLING,
GL_CULL_FACE);
@ -858,10 +854,6 @@ cogl_begin_gl (void)
options.flags = 0;
_cogl_material_flush_gl_state (ctx->source_material, &options);
/* FIXME: This api is a bit yukky, ideally it will be removed if we
* re-work the _cogl_enable mechanism */
enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material);
if (ctx->enable_backface_culling)
enable_flags |= COGL_ENABLE_BACKFACE_CULLING;