From 96b0e6c3047aa6361da173fe5221434920b3c8fa Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sat, 10 Jul 2010 03:40:47 +0100 Subject: [PATCH] material: splits out all the state flushing code This moves the code supporting _cogl_material_flush_gl_state into cogl-material-opengl.c as part of an effort to reduce the size of cogl-material.c to keep it manageable. --- cogl/cogl-journal.c | 1 + cogl/cogl-material-opengl-private.h | 7 + cogl/cogl-material-opengl.c | 1014 +++++++++++++++++++++++++ cogl/cogl-material-private.h | 26 +- cogl/cogl-material.c | 1061 ++------------------------- cogl/cogl-path.c | 1 + cogl/cogl-primitives.c | 1 + cogl/cogl-vertex-buffer.c | 1 + cogl/cogl.c | 1 + cogl/driver/gl/cogl-program.c | 1 + 10 files changed, 1088 insertions(+), 1026 deletions(-) diff --git a/cogl/cogl-journal.c b/cogl/cogl-journal.c index f8ee00402..048461144 100644 --- a/cogl/cogl-journal.c +++ b/cogl/cogl-journal.c @@ -31,6 +31,7 @@ #include "cogl-journal-private.h" #include "cogl-texture-private.h" #include "cogl-material-private.h" +#include "cogl-material-opengl-private.h" #include "cogl-vertex-buffer-private.h" #include "cogl-framebuffer-private.h" #include "cogl-profile.h" diff --git a/cogl/cogl-material-opengl-private.h b/cogl/cogl-material-opengl-private.h index 279f8bd00..fdd5a19a6 100644 --- a/cogl/cogl-material-opengl-private.h +++ b/cogl/cogl-material-opengl-private.h @@ -144,5 +144,12 @@ _cogl_bind_gl_texture_transient (GLenum gl_target, void _cogl_delete_gl_texture (GLuint gl_texture); +void +_cogl_gl_use_program_wrapper (GLuint program); + +void +_cogl_material_flush_gl_state (CoglMaterial *material, + gboolean skip_gl_state); + #endif /* __COGL_MATERIAL_OPENGL_PRIVATE_H */ diff --git a/cogl/cogl-material-opengl.c b/cogl/cogl-material-opengl.c index 06be8c274..236196d04 100644 --- a/cogl/cogl-material-opengl.c +++ b/cogl/cogl-material-opengl.c @@ -34,6 +34,59 @@ #include "cogl-material-opengl-private.h" #include "cogl-material-private.h" #include "cogl-context.h" +#include "cogl-texture-private.h" +#ifndef HAVE_COGL_GLES +#include "cogl-program.h" +#endif + +#ifdef COGL_MATERIAL_BACKEND_GLSL +#include "cogl-material-glsl-private.h" +#endif +#ifdef COGL_MATERIAL_BACKEND_ARBFP +#include "cogl-material-arbfp-private.h" +#endif +#ifdef COGL_MATERIAL_BACKEND_FIXED +#include "cogl-material-fixed-private.h" +#endif + +#include +#include + +/* + * GL/GLES compatability defines for material thingies: + */ + +#ifdef HAVE_COGL_GLES2 +#include "../gles/cogl-gles2-wrapper.h" +#endif + +#ifdef HAVE_COGL_GL +#define glActiveTexture ctx->drv.pf_glActiveTexture +#define glClientActiveTexture ctx->drv.pf_glClientActiveTexture +#define glBlendFuncSeparate ctx->drv.pf_glBlendFuncSeparate +#define glBlendEquation ctx->drv.pf_glBlendEquation +#define glBlendColor ctx->drv.pf_glBlendColor +#define glBlendEquationSeparate ctx->drv.pf_glBlendEquationSeparate + +#define glProgramString ctx->drv.pf_glProgramString +#define glBindProgram ctx->drv.pf_glBindProgram +#define glDeletePrograms ctx->drv.pf_glDeletePrograms +#define glGenPrograms ctx->drv.pf_glGenPrograms +#define glProgramLocalParameter4fv ctx->drv.pf_glProgramLocalParameter4fv +#define glUseProgram ctx->drv.pf_glUseProgram +#endif + +/* These aren't defined in the GLES headers */ +#ifndef GL_POINT_SPRITE +#define GL_POINT_SPRITE 0x8861 +#endif +#ifndef GL_COORD_REPLACE +#define GL_COORD_REPLACE 0x8862 +#endif +#ifndef GL_CLAMP_TO_BORDER +#define GL_CLAMP_TO_BORDER 0x812d +#endif + static void texture_unit_init (CoglTextureUnit *unit, int index_) @@ -227,4 +280,965 @@ _cogl_material_texture_storage_change_notify (CoglHandle texture) } } +void +_cogl_gl_use_program_wrapper (GLuint program) +{ +#ifdef COGL_MATERIAL_BACKEND_GLSL + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->current_gl_program == program) + return; + + if (program) + { + GLenum gl_error; + + while ((gl_error = glGetError ()) != GL_NO_ERROR) + ; + glUseProgram (program); + if (glGetError () != GL_NO_ERROR) + { + GE (glUseProgram (0)); + ctx->current_gl_program = 0; + return; + } + } + else + GE (glUseProgram (0)); + + ctx->current_gl_program = program; +#endif +} + +static void +disable_glsl (void) +{ +#ifdef COGL_MATERIAL_BACKEND_GLSL + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_GLSL) + _cogl_gl_use_program_wrapper (0); +#endif +} + +static void +disable_arbfp (void) +{ +#ifdef COGL_MATERIAL_BACKEND_ARBFP + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_ARBFP) + GE (glDisable (GL_FRAGMENT_PROGRAM_ARB)); +#endif +} + +void +_cogl_use_program (CoglHandle program_handle, CoglMaterialProgramType type) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + switch (type) + { +#ifdef COGL_MATERIAL_BACKEND_GLSL + case COGL_MATERIAL_PROGRAM_TYPE_GLSL: + { + /* The GLES2 backend currently manages its own codegen for + * fixed function API fallbacks and manages its own shader + * state. */ +#ifndef HAVE_COGL_GLES2 + CoglProgram *program = + _cogl_program_pointer_from_handle (program_handle); + + _cogl_gl_use_program_wrapper (program->gl_handle); + disable_arbfp (); +#endif + + ctx->current_use_program_type = type; + break; + } +#else + case COGL_MATERIAL_PROGRAM_TYPE_GLSL: + g_warning ("Unexpected use of GLSL backend!"); + break; +#endif +#ifdef COGL_MATERIAL_BACKEND_ARBFP + case COGL_MATERIAL_PROGRAM_TYPE_ARBFP: + + /* _cogl_gl_use_program_wrapper can be called by cogl-program.c + * so we can't bailout without making sure we glUseProgram (0) + * first. */ + disable_glsl (); + + if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_ARBFP) + break; + + GE (glEnable (GL_FRAGMENT_PROGRAM_ARB)); + + ctx->current_use_program_type = type; + break; +#else + case COGL_MATERIAL_PROGRAM_TYPE_ARBFP: + g_warning ("Unexpected use of GLSL backend!"); + break; +#endif +#ifdef COGL_MATERIAL_BACKEND_FIXED + case COGL_MATERIAL_PROGRAM_TYPE_FIXED: + + /* _cogl_gl_use_program_wrapper can be called by cogl-program.c + * so we can't bailout without making sure we glUseProgram (0) + * first. */ + disable_glsl (); + + if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_FIXED) + break; + + disable_arbfp (); + + ctx->current_use_program_type = type; +#endif + } +} + +#if defined (COGL_MATERIAL_BACKEND_GLSL) || \ + defined (COGL_MATERIAL_BACKEND_ARBFP) +int +_cogl_get_max_texture_image_units (void) +{ + _COGL_GET_CONTEXT (ctx, 0); + + /* This function is called quite often so we cache the value to + avoid too many GL calls */ + if (G_UNLIKELY (ctx->max_texture_image_units == -1)) + { + ctx->max_texture_image_units = 1; + GE (glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, + &ctx->max_texture_image_units)); + } + + return ctx->max_texture_image_units; +} +#endif + +static void +_cogl_material_layer_get_texture_info (CoglMaterialLayer *layer, + CoglHandle *texture, + GLuint *gl_texture, + GLuint *gl_target) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + *texture = layer->texture; + if (G_UNLIKELY (*texture == COGL_INVALID_HANDLE)) + *texture = ctx->default_gl_texture_2d_tex; + if (layer->texture_overridden) + { + *gl_texture = layer->slice_gl_texture; + *gl_target = layer->slice_gl_target; + } + else + cogl_texture_get_gl_texture (*texture, gl_texture, gl_target); +} + +#ifndef HAVE_COGL_GLES + +static gboolean +blend_factor_uses_constant (GLenum blend_factor) +{ + return (blend_factor == GL_CONSTANT_COLOR || + blend_factor == GL_ONE_MINUS_CONSTANT_COLOR || + blend_factor == GL_CONSTANT_ALPHA || + blend_factor == GL_ONE_MINUS_CONSTANT_ALPHA); +} + +#endif + +static void +flush_depth_state (CoglMaterialDepthState *depth_state) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->depth_test_function_cache != depth_state->depth_test_function) + { + GE (glDepthFunc (depth_state->depth_test_function)); + ctx->depth_test_function_cache = depth_state->depth_test_function; + } + + if (ctx->depth_writing_enabled_cache != depth_state->depth_writing_enabled) + { + GE (glDepthMask (depth_state->depth_writing_enabled ? + GL_TRUE : GL_FALSE)); + ctx->depth_writing_enabled_cache = depth_state->depth_writing_enabled; + } + +#ifndef COGL_HAS_GLES + if (ctx->depth_range_near_cache != depth_state->depth_range_near || + ctx->depth_range_far_cache != depth_state->depth_range_far) + { +#ifdef COGL_HAS_GLES2 + GE (glDepthRangef (depth_state->depth_range_near, + depth_state->depth_range_far)); +#else + GE (glDepthRange (depth_state->depth_range_near, + depth_state->depth_range_far)); +#endif + ctx->depth_range_near_cache = depth_state->depth_range_near; + ctx->depth_range_far_cache = depth_state->depth_range_far; + } +#endif /* COGL_HAS_GLES */ +} + +static void +_cogl_material_flush_color_blend_alpha_depth_state ( + CoglMaterial *material, + unsigned long materials_difference, + gboolean skip_gl_color) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (!skip_gl_color) + { + if ((materials_difference & COGL_MATERIAL_STATE_COLOR) || + /* Assume if we were previously told to skip the color, then + * the current color needs updating... */ + ctx->current_material_skip_gl_color) + { + CoglMaterial *authority = + _cogl_material_get_authority (material, COGL_MATERIAL_STATE_COLOR); + GE (glColor4ub (cogl_color_get_red_byte (&authority->color), + cogl_color_get_green_byte (&authority->color), + cogl_color_get_blue_byte (&authority->color), + cogl_color_get_alpha_byte (&authority->color))); + } + } + + if (materials_difference & COGL_MATERIAL_STATE_LIGHTING) + { + CoglMaterial *authority = + _cogl_material_get_authority (material, COGL_MATERIAL_STATE_LIGHTING); + CoglMaterialLightingState *lighting_state = + &authority->big_state->lighting_state; + + /* FIXME - we only need to set these if lighting is enabled... */ + GLfloat shininess = lighting_state->shininess * 128.0f; + + GE (glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, lighting_state->ambient)); + GE (glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, lighting_state->diffuse)); + GE (glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, lighting_state->specular)); + GE (glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, lighting_state->emission)); + GE (glMaterialfv (GL_FRONT_AND_BACK, GL_SHININESS, &shininess)); + } + + if (materials_difference & COGL_MATERIAL_STATE_BLEND) + { + CoglMaterial *authority = + _cogl_material_get_authority (material, COGL_MATERIAL_STATE_BLEND); + CoglMaterialBlendState *blend_state = + &authority->big_state->blend_state; + +#if defined (HAVE_COGL_GLES2) + gboolean have_blend_equation_seperate = TRUE; + gboolean have_blend_func_separate = TRUE; +#elif defined (HAVE_COGL_GL) + gboolean have_blend_equation_seperate = FALSE; + gboolean have_blend_func_separate = FALSE; + if (ctx->drv.pf_glBlendEquationSeparate) /* Only GL 2.0 + */ + have_blend_equation_seperate = TRUE; + if (ctx->drv.pf_glBlendFuncSeparate) /* Only GL 1.4 + */ + have_blend_func_separate = TRUE; +#endif + +#ifndef HAVE_COGL_GLES /* GLES 1 only has glBlendFunc */ + if (blend_factor_uses_constant (blend_state->blend_src_factor_rgb) || + blend_factor_uses_constant (blend_state->blend_src_factor_alpha) || + blend_factor_uses_constant (blend_state->blend_dst_factor_rgb) || + blend_factor_uses_constant (blend_state->blend_dst_factor_alpha)) + { + float red = + cogl_color_get_red_float (&blend_state->blend_constant); + float green = + cogl_color_get_green_float (&blend_state->blend_constant); + float blue = + cogl_color_get_blue_float (&blend_state->blend_constant); + float alpha = + cogl_color_get_alpha_float (&blend_state->blend_constant); + + + GE (glBlendColor (red, green, blue, alpha)); + } + + if (have_blend_equation_seperate && + blend_state->blend_equation_rgb != blend_state->blend_equation_alpha) + GE (glBlendEquationSeparate (blend_state->blend_equation_rgb, + blend_state->blend_equation_alpha)); + else + GE (glBlendEquation (blend_state->blend_equation_rgb)); + + if (have_blend_func_separate && + (blend_state->blend_src_factor_rgb != blend_state->blend_src_factor_alpha || + (blend_state->blend_src_factor_rgb != + blend_state->blend_src_factor_alpha))) + GE (glBlendFuncSeparate (blend_state->blend_src_factor_rgb, + blend_state->blend_dst_factor_rgb, + blend_state->blend_src_factor_alpha, + blend_state->blend_dst_factor_alpha)); + else +#endif + GE (glBlendFunc (blend_state->blend_src_factor_rgb, + blend_state->blend_dst_factor_rgb)); + } + + if (materials_difference & COGL_MATERIAL_STATE_ALPHA_FUNC) + { + CoglMaterial *authority = + _cogl_material_get_authority (material, COGL_MATERIAL_STATE_ALPHA_FUNC); + CoglMaterialAlphaFuncState *alpha_state = + &authority->big_state->alpha_state; + + /* NB: Currently the Cogl defines are compatible with the GL ones: */ + GE (glAlphaFunc (alpha_state->alpha_func, + alpha_state->alpha_func_reference)); + } + + if (materials_difference & COGL_MATERIAL_STATE_DEPTH) + { + CoglMaterial *authority = + _cogl_material_get_authority (material, COGL_MATERIAL_STATE_DEPTH); + CoglMaterialDepthState *depth_state = &authority->big_state->depth_state; + + if (depth_state->depth_test_enabled) + { + if (ctx->depth_test_enabled_cache != TRUE) + { + GE (glEnable (GL_DEPTH_TEST)); + ctx->depth_test_enabled_cache = depth_state->depth_test_enabled; + } + flush_depth_state (depth_state); + } + else if (ctx->depth_test_enabled_cache != FALSE) + { + GE (glDisable (GL_DEPTH_TEST)); + ctx->depth_test_enabled_cache = depth_state->depth_test_enabled; + } + } + + if (materials_difference & COGL_MATERIAL_STATE_POINT_SIZE) + { + CoglMaterial *authority = + _cogl_material_get_authority (material, COGL_MATERIAL_STATE_POINT_SIZE); + + if (ctx->point_size_cache != authority->big_state->point_size) + { + GE( glPointSize (authority->big_state->point_size) ); + ctx->point_size_cache = authority->big_state->point_size; + } + } + + if (material->real_blend_enable != ctx->gl_blend_enable_cache) + { + if (material->real_blend_enable) + GE (glEnable (GL_BLEND)); + else + GE (glDisable (GL_BLEND)); + /* XXX: we shouldn't update any other blend state if blending + * is disabled! */ + ctx->gl_blend_enable_cache = material->real_blend_enable; + } +} + +static int +get_max_activateable_texture_units (void) +{ + _COGL_GET_CONTEXT (ctx, 0); + + if (G_UNLIKELY (ctx->max_activateable_texture_units == -1)) + { +#ifdef HAVE_COGL_GL + GLint max_tex_coords; + GLint max_combined_tex_units; + GE (glGetIntegerv (GL_MAX_TEXTURE_COORDS, &max_tex_coords)); + GE (glGetIntegerv (GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, + &max_combined_tex_units)); + ctx->max_activateable_texture_units = + MAX (max_tex_coords - 1, max_combined_tex_units); +#else + GE (glGetIntegerv (GL_MAX_TEXTURE_UNITS, + &ctx->max_activateable_texture_units)); +#endif + } + + return ctx->max_activateable_texture_units; +} + +typedef struct +{ + int i; + unsigned long *layer_differences; +} CoglMaterialFlushLayerState; + +static gboolean +flush_layers_common_gl_state_cb (CoglMaterialLayer *layer, void *user_data) +{ + CoglMaterialFlushLayerState *flush_state = user_data; + int unit_index = flush_state->i; + CoglTextureUnit *unit = _cogl_get_texture_unit (unit_index); + unsigned long layers_difference = + flush_state->layer_differences[unit_index]; + + /* There may not be enough texture units so we can bail out if + * that's the case... + */ + if (G_UNLIKELY (unit_index >= get_max_activateable_texture_units ())) + { + static gboolean shown_warning = FALSE; + + if (!shown_warning) + { + g_warning ("Your hardware does not have enough texture units" + "to handle this many texture layers"); + shown_warning = TRUE; + } + return FALSE; + } + + if (layers_difference & COGL_MATERIAL_LAYER_STATE_TEXTURE) + { + CoglMaterialLayer *authority = + _cogl_material_layer_get_authority (layer, + COGL_MATERIAL_LAYER_STATE_TEXTURE); + CoglHandle texture = NULL; + GLuint gl_texture; + GLenum gl_target; + + _cogl_material_layer_get_texture_info (authority, + &texture, + &gl_texture, + &gl_target); + + _cogl_set_active_texture_unit (unit_index); + + /* NB: There are several Cogl components and some code in + * Clutter that will temporarily bind arbitrary GL textures to + * query and modify texture object parameters. If you look at + * _cogl_bind_gl_texture_transient() you can see we make sure + * that such code always binds to texture unit 1 which means we + * can't rely on the unit->gl_texture state if unit->index == 1. + * + * Because texture unit 1 is a bit special we actually defer any + * necessary glBindTexture for it until the end of + * _cogl_material_flush_gl_state(). + * + * NB: we get notified whenever glDeleteTextures is used (see + * _cogl_delete_gl_texture()) where we invalidate + * unit->gl_texture references to deleted textures so it's safe + * to compare unit->gl_texture with gl_texture. (Without the + * hook it would be possible to delete a GL texture and create a + * new one with the same name and comparing unit->gl_texture and + * gl_texture wouldn't detect that.) + * + * NB: for foreign textures we don't know how the deletion of + * the GL texture objects correspond to the deletion of the + * CoglTextures so if there was previously a foreign texture + * associated with the texture unit then we can't assume that we + * aren't seeing a recycled texture name so we have to bind. + */ + if (unit->gl_texture != gl_texture || unit->is_foreign) + { + if (unit_index != 1) + GE (glBindTexture (gl_target, gl_texture)); + unit->gl_texture = gl_texture; + } + + unit->is_foreign = _cogl_texture_is_foreign (texture); + + /* Disable the previous target if it was different and it's + * still enabled */ + if (unit->enabled && unit->current_gl_target != gl_target) + GE (glDisable (unit->current_gl_target)); + + if (!G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_TEXTURING) && + (!unit->enabled || unit->current_gl_target != gl_target)) + { + GE (glEnable (gl_target)); + unit->enabled = TRUE; + unit->current_gl_target = gl_target; + } + + /* The texture_storage_changed boolean indicates if the + * CoglTexture's underlying GL texture storage has changed since + * it was flushed to the texture unit. We've just flushed the + * latest state so we can reset this. */ + unit->texture_storage_changed = FALSE; + } + else + { + /* Even though there may be no difference between the last flushed + * texture state and the current layers texture state it may be that the + * texture unit has been disabled for some time so we need to assert that + * it's enabled now. + */ + if (!G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_TEXTURING) && + !unit->enabled) + { + GE (glEnable (unit->current_gl_target)); + unit->enabled = TRUE; + } + } + + if (layers_difference & COGL_MATERIAL_LAYER_STATE_USER_MATRIX) + { + CoglMaterialLayerState state = COGL_MATERIAL_LAYER_STATE_USER_MATRIX; + CoglMaterialLayer *authority = + _cogl_material_layer_get_authority (layer, state); + + _cogl_matrix_stack_set (unit->matrix_stack, + &authority->big_state->matrix); + + _cogl_matrix_stack_flush_to_gl (unit->matrix_stack, COGL_MATRIX_TEXTURE); + } + + if (layers_difference & COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS) + { + CoglMaterialState change = COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS; + CoglMaterialLayer *authority = + _cogl_material_layer_get_authority (layer, change); + CoglMaterialLayerBigState *big_state = authority->big_state; + + _cogl_set_active_texture_unit (unit_index); + + GE (glTexEnvi (GL_POINT_SPRITE, GL_COORD_REPLACE, + big_state->point_sprite_coords)); + } + + cogl_handle_ref (layer); + if (unit->layer != COGL_INVALID_HANDLE) + cogl_handle_unref (unit->layer); + + unit->layer = layer; + unit->layer_changes_since_flush = 0; + + flush_state->i++; + + return TRUE; +} + +static void +_cogl_material_flush_common_gl_state (CoglMaterial *material, + unsigned long materials_difference, + unsigned long *layer_differences, + gboolean skip_gl_color) +{ + CoglMaterialFlushLayerState state; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_material_flush_color_blend_alpha_depth_state (material, + materials_difference, + skip_gl_color); + + state.i = 0; + state.layer_differences = layer_differences; + _cogl_material_foreach_layer (material, + flush_layers_common_gl_state_cb, + &state); + + /* Disable additional texture units that may have previously been in use.. */ + for (; state.i < ctx->texture_units->len; state.i++) + _cogl_disable_texture_unit (state.i); +} + +/* Re-assert the layer's wrap modes on the given CoglTexture. + * + * Note: we don't simply forward the wrap modes to layer->texture + * since the actual texture being used may have been overridden. + */ +static void +_cogl_material_layer_forward_wrap_modes (CoglMaterialLayer *layer, + CoglHandle texture) +{ + CoglMaterialWrapModeInternal wrap_mode_s, wrap_mode_t, wrap_mode_p; + GLenum gl_wrap_mode_s, gl_wrap_mode_t, gl_wrap_mode_p; + + if (texture == COGL_INVALID_HANDLE) + return; + + _cogl_material_layer_get_wrap_modes (layer, + &wrap_mode_s, + &wrap_mode_t, + &wrap_mode_p); + + /* Update the wrap mode on the texture object. The texture backend + should cache the value so that it will be a no-op if the object + already has the same wrap mode set. The backend is best placed to + do this because it knows how many of the coordinates will + actually be used (ie, a 1D texture only cares about the 's' + coordinate but a 3D texture would use all three). GL uses the + wrap mode as part of the texture object state but we are + pretending it's part of the per-layer environment state. This + will break if the application tries to use different modes in + different layers using the same texture. */ + + if (wrap_mode_s == COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC) + gl_wrap_mode_s = GL_CLAMP_TO_EDGE; + else + gl_wrap_mode_s = wrap_mode_s; + + if (wrap_mode_t == COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC) + gl_wrap_mode_t = GL_CLAMP_TO_EDGE; + else + gl_wrap_mode_t = wrap_mode_t; + + if (wrap_mode_p == COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC) + gl_wrap_mode_p = GL_CLAMP_TO_EDGE; + else + gl_wrap_mode_p = wrap_mode_p; + + _cogl_texture_set_wrap_mode_parameters (texture, + gl_wrap_mode_s, + gl_wrap_mode_t, + gl_wrap_mode_p); +} + +/* OpenGL associates the min/mag filters and repeat modes with the + * texture object not the texture unit so we always have to re-assert + * the filter and repeat modes whenever we use a texture since it may + * be referenced by multiple materials with different modes. + * + * XXX: GL_ARB_sampler_objects fixes this in OpenGL so we should + * eventually look at using this extension when available. + */ +static void +foreach_texture_unit_update_filter_and_wrap_modes (void) +{ + int i; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + for (i = 0; i < ctx->texture_units->len; i++) + { + CoglTextureUnit *unit = + &g_array_index (ctx->texture_units, CoglTextureUnit, i); + + if (!unit->enabled) + break; + + if (unit->layer) + { + CoglHandle texture = _cogl_material_layer_get_texture (unit->layer); + CoglMaterialFilter min; + CoglMaterialFilter mag; + + _cogl_material_layer_get_filters (unit->layer, &min, &mag); + _cogl_texture_set_filters (texture, min, mag); + + _cogl_material_layer_forward_wrap_modes (unit->layer, texture); + } + } +} + +typedef struct +{ + int i; + unsigned long *layer_differences; +} CoglMaterialCompareLayersState; + +static gboolean +compare_layer_differences_cb (CoglMaterialLayer *layer, void *user_data) +{ + CoglMaterialCompareLayersState *state = user_data; + CoglTextureUnit *unit = _cogl_get_texture_unit (state->i); + + if (unit->layer == layer) + state->layer_differences[state->i] = unit->layer_changes_since_flush; + else if (unit->layer) + { + state->layer_differences[state->i] = unit->layer_changes_since_flush; + state->layer_differences[state->i] |= + _cogl_material_layer_compare_differences (layer, unit->layer); + } + else + state->layer_differences[state->i] = COGL_MATERIAL_LAYER_STATE_ALL_SPARSE; + + /* XXX: There is always a possibility that a CoglTexture's + * underlying GL texture storage has been changed since it was last + * bound to a texture unit which is why we have a callback into + * _cogl_material_texture_storage_change_notify whenever a textures + * underlying GL texture storage changes which will set the + * unit->texture_intern_changed flag. If we see that's been set here + * then we force an update of the texture state... + */ + if (unit->texture_storage_changed) + state->layer_differences[state->i] |= COGL_MATERIAL_LAYER_STATE_TEXTURE; + + state->i++; + + return TRUE; +} + +typedef struct +{ + const CoglMaterialBackend *backend; + CoglMaterial *material; + unsigned long *layer_differences; + gboolean error_adding_layer; + gboolean added_layer; +} CoglMaterialBackendAddLayerState; + + +static gboolean +backend_add_layer_cb (CoglMaterialLayer *layer, + void *user_data) +{ + CoglMaterialBackendAddLayerState *state = user_data; + const CoglMaterialBackend *backend = state->backend; + CoglMaterial *material = state->material; + int unit_index = _cogl_material_layer_get_unit_index (layer); + CoglTextureUnit *unit = _cogl_get_texture_unit (unit_index); + + _COGL_GET_CONTEXT (ctx, FALSE); + + /* NB: We don't support the random disabling of texture + * units, so as soon as we hit a disabled unit we know all + * subsequent units are also disabled */ + if (!unit->enabled) + return FALSE; + + if (G_UNLIKELY (unit_index >= backend->get_max_texture_units ())) + { + int j; + for (j = unit_index; j < ctx->texture_units->len; j++) + _cogl_disable_texture_unit (j); + /* TODO: although this isn't considered an error that + * warrants falling back to a different backend we + * should print a warning here. */ + return FALSE; + } + + /* Either generate per layer code snippets or setup the + * fixed function glTexEnv for each layer... */ + if (G_LIKELY (backend->add_layer (material, + layer, + state->layer_differences[unit_index]))) + state->added_layer = TRUE; + else + { + state->error_adding_layer = TRUE; + return FALSE; + } + + return TRUE; +} + +/* + * _cogl_material_flush_gl_state: + * + * Details of override options: + * ->fallback_mask: is a bitmask of the material layers that need to be + * replaced with the default, fallback textures. The fallback textures are + * fully transparent textures so they hopefully wont contribute to the + * texture combining. + * + * The intention of fallbacks is to try and preserve + * the number of layers the user is expecting so that texture coordinates + * they gave will mostly still correspond to the textures they intended, and + * have a fighting chance of looking close to their originally intended + * result. + * + * ->disable_mask: is a bitmask of the material layers that will simply have + * texturing disabled. It's only really intended for disabling all layers + * > X; i.e. we'd expect to see a contiguous run of 0 starting from the LSB + * and at some point the remaining bits flip to 1. It might work to disable + * arbitrary layers; though I'm not sure a.t.m how OpenGL would take to + * that. + * + * The intention of the disable_mask is for emitting geometry when the user + * hasn't supplied enough texture coordinates for all the layers and it's + * not possible to auto generate default texture coordinates for those + * layers. + * + * ->layer0_override_texture: forcibly tells us to bind this GL texture name for + * layer 0 instead of plucking the gl_texture from the CoglTexture of layer + * 0. + * + * The intention of this is for any primitives that supports sliced textures. + * The code will can iterate each of the slices and re-flush the material + * forcing the GL texture of each slice in turn. + * + * ->wrap_mode_overrides: overrides the wrap modes set on each + * layer. This is used to implement the automatic wrap mode. + * + * XXX: It might also help if we could specify a texture matrix for code + * dealing with slicing that would be multiplied with the users own matrix. + * + * Normaly texture coords in the range [0, 1] refer to the extents of the + * texture, but when your GL texture represents a slice of the real texture + * (from the users POV) then a texture matrix would be a neat way of + * transforming the mapping for each slice. + * + * Currently for textured rectangles we manually calculate the texture + * coords for each slice based on the users given coords, but this solution + * isn't ideal, and can't be used with CoglVertexBuffers. + */ +void +_cogl_material_flush_gl_state (CoglMaterial *material, + gboolean skip_gl_color) +{ + unsigned long materials_difference; + int n_layers; + unsigned long *layer_differences = NULL; + int i; + CoglTextureUnit *unit1; + + COGL_STATIC_TIMER (material_flush_timer, + "Mainloop", /* parent */ + "Material Flush", + "The time spent flushing material state", + 0 /* no application private data */); + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + COGL_TIMER_START (_cogl_uprof_context, material_flush_timer); + + if (ctx->current_material == material) + materials_difference = ctx->current_material_changes_since_flush; + else if (ctx->current_material) + { + materials_difference = ctx->current_material_changes_since_flush; + materials_difference |= + _cogl_material_compare_differences (ctx->current_material, + material); + } + else + materials_difference = COGL_MATERIAL_STATE_ALL_SPARSE; + + /* Get a layer_differences mask for each layer to be flushed */ + n_layers = cogl_material_get_n_layers (material); + if (n_layers) + { + CoglMaterialCompareLayersState state; + layer_differences = g_alloca (sizeof (unsigned long *) * n_layers); + memset (layer_differences, 0, sizeof (layer_differences)); + state.i = 0; + state.layer_differences = layer_differences; + _cogl_material_foreach_layer (material, + compare_layer_differences_cb, + &state); + } + + /* First flush everything that's the same regardless of which + * material backend is being used... + * + * 1) top level state: + * glColor (or skip if a vertex attribute is being used for color) + * blend state + * alpha test state (except for GLES 2.0) + * + * 2) then foreach layer: + * determine gl_target/gl_texture + * bind texture + * enable/disable target + * flush user matrix + * + * Note: After _cogl_material_flush_common_gl_state you can expect + * all state of the layers corresponding texture unit to be + * updated. + */ + _cogl_material_flush_common_gl_state (material, + materials_difference, + layer_differences, + skip_gl_color); + + /* Now flush the fragment processing state according to the current + * fragment processing backend. + * + * Note: Some of the backends may not support the current material + * configuration and in that case it will report an error and we + * will fallback to a different backend. + * + * NB: if material->backend != COGL_MATERIAL_BACKEND_UNDEFINED then + * we have previously managed to successfully flush this material + * with the given backend so we will simply use that to avoid + * fallback code paths. + */ + + if (material->backend == COGL_MATERIAL_BACKEND_UNDEFINED) + _cogl_material_set_backend (material, COGL_MATERIAL_BACKEND_DEFAULT); + + for (i = material->backend; + i < G_N_ELEMENTS (_cogl_material_backends); + i++, _cogl_material_set_backend (material, i)) + { + const CoglMaterialBackend *backend = _cogl_material_backends[i]; + CoglMaterialBackendAddLayerState state; + + /* E.g. For backends generating code they can setup their + * scratch buffers here... */ + if (G_UNLIKELY (!backend->start (material, + n_layers, + materials_difference))) + continue; + + state.backend = backend; + state.material = material; + state.layer_differences = layer_differences; + state.error_adding_layer = FALSE; + state.added_layer = FALSE; + _cogl_material_foreach_layer (material, + backend_add_layer_cb, + &state); + + if (G_UNLIKELY (state.error_adding_layer)) + continue; + + if (!state.added_layer && + backend->passthrough && + G_UNLIKELY (!backend->passthrough (material))) + continue; + + /* For backends generating code they may compile and link their + * programs here, update any uniforms and tell OpenGL to use + * that program. + */ + if (G_UNLIKELY (!backend->end (material, materials_difference))) + continue; + + break; + } + + /* FIXME: This reference is actually resulting in lots of + * copy-on-write reparenting because one-shot materials end up + * living for longer than necessary and so any later modification of + * the parent will cause a copy-on-write. + * + * XXX: The issue should largely go away when we switch to using + * weak materials for overrides. + */ + cogl_object_ref (material); + if (ctx->current_material != NULL) + cogl_object_unref (ctx->current_material); + ctx->current_material = material; + ctx->current_material_changes_since_flush = 0; + ctx->current_material_skip_gl_color = skip_gl_color; + + /* Handle the fact that OpenGL associates texture filter and wrap + * modes with the texture objects not the texture units... */ + foreach_texture_unit_update_filter_and_wrap_modes (); + + /* If this material has more than one layer then we always need + * to make sure we rebind the texture for unit 1. + * + * NB: various components of Cogl may temporarily bind arbitrary + * textures to texture unit 1 so they can query and modify texture + * object parameters. cogl-material.c (See + * _cogl_bind_gl_texture_transient) + */ + unit1 = _cogl_get_texture_unit (1); + if (unit1->enabled && unit1->dirty_gl_texture) + { + _cogl_set_active_texture_unit (1); + GE (glBindTexture (unit1->current_gl_target, unit1->gl_texture)); + unit1->dirty_gl_texture = FALSE; + } + + COGL_TIMER_STOP (_cogl_uprof_context, material_flush_timer); +} diff --git a/cogl/cogl-material-private.h b/cogl/cogl-material-private.h index 54fb380bc..54b14b8e5 100644 --- a/cogl/cogl-material-private.h +++ b/cogl/cogl-material-private.h @@ -33,6 +33,7 @@ #include "cogl-material.h" #include "cogl-matrix.h" #include "cogl-handle.h" +#include "cogl-profile.h" #include @@ -551,6 +552,9 @@ typedef enum COGL_MATERIAL_PROGRAM_TYPE_FIXED } CoglMaterialProgramType; +extern const CoglMaterialBackend * +_cogl_material_backends[COGL_MATERIAL_N_BACKENDS]; + void _cogl_material_init_default_material (void); @@ -681,6 +685,8 @@ _cogl_use_program (CoglHandle program_handle, CoglMaterialProgramType type); unsigned int _cogl_get_n_args_for_combine_func (GLint func); +void +_cogl_material_set_backend (CoglMaterial *material, int backend); CoglMaterial * _cogl_material_get_parent (CoglMaterial *material); @@ -689,9 +695,9 @@ void _cogl_material_get_colorubv (CoglMaterial *material, guint8 *color); -void -_cogl_material_flush_gl_state (CoglMaterial *material, - gboolean skip_gl_state); +unsigned long +_cogl_material_compare_differences (CoglMaterial *material0, + CoglMaterial *material1); gboolean _cogl_material_equal (CoglMaterial *material0, @@ -704,6 +710,17 @@ _cogl_material_journal_ref (CoglMaterial *material); void _cogl_material_journal_unref (CoglMaterial *material); +void +_cogl_material_layer_get_wrap_modes (CoglMaterialLayer *layer, + CoglMaterialWrapModeInternal *wrap_mode_s, + CoglMaterialWrapModeInternal *wrap_mode_t, + CoglMaterialWrapModeInternal *wrap_mode_r); + +void +_cogl_material_layer_get_filters (CoglMaterialLayer *layer, + CoglMaterialFilter *min_filter, + CoglMaterialFilter *mag_filter); + void _cogl_material_set_user_program (CoglMaterial *material, CoglHandle program); @@ -714,9 +731,6 @@ _cogl_material_texture_storage_change_notify (CoglHandle texture); void _cogl_material_apply_legacy_state (CoglMaterial *material); -void -_cogl_gl_use_program_wrapper (GLuint program); - void _cogl_material_apply_overrides (CoglMaterial *material, CoglMaterialFlushOptions *options); diff --git a/cogl/cogl-material.c b/cogl/cogl-material.c index a32ce1e9e..f266105cb 100644 --- a/cogl/cogl-material.c +++ b/cogl/cogl-material.c @@ -41,49 +41,11 @@ #include "cogl-journal-private.h" #include "cogl-color-private.h" #include "cogl-profile.h" -#ifndef HAVE_COGL_GLES -#include "cogl-program.h" -#endif #include #include #include -/* - * GL/GLES compatability defines for material thingies: - */ - -#ifdef HAVE_COGL_GLES2 -#include "../gles/cogl-gles2-wrapper.h" -#endif - -#ifdef HAVE_COGL_GL -#define glActiveTexture ctx->drv.pf_glActiveTexture -#define glClientActiveTexture ctx->drv.pf_glClientActiveTexture -#define glBlendFuncSeparate ctx->drv.pf_glBlendFuncSeparate -#define glBlendEquation ctx->drv.pf_glBlendEquation -#define glBlendColor ctx->drv.pf_glBlendColor -#define glBlendEquationSeparate ctx->drv.pf_glBlendEquationSeparate - -#define glProgramString ctx->drv.pf_glProgramString -#define glBindProgram ctx->drv.pf_glBindProgram -#define glDeletePrograms ctx->drv.pf_glDeletePrograms -#define glGenPrograms ctx->drv.pf_glGenPrograms -#define glProgramLocalParameter4fv ctx->drv.pf_glProgramLocalParameter4fv -#define glUseProgram ctx->drv.pf_glUseProgram -#endif - -/* These aren't defined in the GLES headers */ -#ifndef GL_POINT_SPRITE -#define GL_POINT_SPRITE 0x8861 -#endif -#ifndef GL_COORD_REPLACE -#define GL_COORD_REPLACE 0x8862 -#endif -#ifndef GL_CLAMP_TO_BORDER -#define GL_CLAMP_TO_BORDER 0x812d -#endif - #define COGL_MATERIAL_LAYER(X) ((CoglMaterialLayer *)(X)) typedef gboolean (*CoglMaterialStateComparitor) (CoglMaterial *authority0, @@ -100,7 +62,7 @@ static void handle_automatic_blend_enable (CoglMaterial *material, CoglMaterialState changes); static void recursively_free_layer_caches (CoglMaterial *material); -static const CoglMaterialBackend *backends[COGL_MATERIAL_N_BACKENDS]; +const CoglMaterialBackend *_cogl_material_backends[COGL_MATERIAL_N_BACKENDS]; #ifdef COGL_MATERIAL_BACKEND_GLSL #include "cogl-material-glsl-private.h" @@ -220,13 +182,16 @@ _cogl_material_init_default_material (void) /* Take this opportunity to setup the fragment processing backends... */ #ifdef COGL_MATERIAL_BACKEND_GLSL - backends[COGL_MATERIAL_BACKEND_GLSL] = &_cogl_material_glsl_backend; + _cogl_material_backends[COGL_MATERIAL_BACKEND_GLSL] = + &_cogl_material_glsl_backend; #endif #ifdef COGL_MATERIAL_BACKEND_ARBFP - backends[COGL_MATERIAL_BACKEND_ARBFP] = &_cogl_material_arbfp_backend; + _cogl_material_backends[COGL_MATERIAL_BACKEND_ARBFP] = + &_cogl_material_arbfp_backend; #endif #ifdef COGL_MATERIAL_BACKEND_FIXED - backends[COGL_MATERIAL_BACKEND_FIXED] = &_cogl_material_fixed_backend; + _cogl_material_backends[COGL_MATERIAL_BACKEND_FIXED] = + &_cogl_material_fixed_backend; #endif _cogl_material_node_init (COGL_MATERIAL_NODE (material)); @@ -361,8 +326,12 @@ _cogl_material_set_parent (CoglMaterial *material, CoglMaterial *parent) * may be notified here... */ if (material->backend != COGL_MATERIAL_BACKEND_UNDEFINED && - backends[material->backend]->material_set_parent_notify) - backends[material->backend]->material_set_parent_notify (material); + _cogl_material_backends[material->backend]->material_set_parent_notify) + { + const CoglMaterialBackend *backend = + _cogl_material_backends[material->backend]; + backend->material_set_parent_notify (material); + } } /* XXX: Always have an eye out for opportunities to lower the cost of @@ -443,8 +412,12 @@ static void _cogl_material_backend_free_priv (CoglMaterial *material) { if (material->backend != COGL_MATERIAL_BACKEND_UNDEFINED && - backends[material->backend]->free_priv) - backends[material->backend]->free_priv (material); + _cogl_material_backends[material->backend]->free_priv) + { + const CoglMaterialBackend *backend = + _cogl_material_backends[material->backend]; + backend->free_priv (material); + } } static void @@ -818,7 +791,7 @@ _cogl_material_needs_blending_enabled (CoglMaterial *material, return FALSE; } -static void +void _cogl_material_set_backend (CoglMaterial *material, int backend) { _cogl_material_backend_free_priv (material); @@ -1042,10 +1015,12 @@ _cogl_material_pre_change_notify (CoglMaterial *material, _cogl_material_set_backend (material, COGL_MATERIAL_BACKEND_UNDEFINED); if (material->backend != COGL_MATERIAL_BACKEND_UNDEFINED && - backends[material->backend]->material_pre_change_notify) - backends[material->backend]->material_pre_change_notify (material, - change, - new_color); + _cogl_material_backends[material->backend]->material_pre_change_notify) + { + const CoglMaterialBackend *backend = + _cogl_material_backends[material->backend]; + backend->material_pre_change_notify (material, change, new_color); + } /* * There is an arbitrary tree of descendants of this material; any of @@ -1327,8 +1302,12 @@ _cogl_material_backend_layer_change_notify (CoglMaterialLayer *layer, */ for (i = 0; i < COGL_MATERIAL_N_BACKENDS; i++) { - if (layer->backend_priv[i] && backends[i]->layer_pre_change_notify) - backends[i]->layer_pre_change_notify (layer, change); + if (layer->backend_priv[i] && + _cogl_material_backends[i]->layer_pre_change_notify) + { + const CoglMaterialBackend *backend = _cogl_material_backends[i]; + backend->layer_pre_change_notify (layer, change); + } } } @@ -2363,7 +2342,7 @@ cogl_material_get_layer_wrap_mode_p (CoglMaterial *material, int layer_index) return cogl_material_layer_get_wrap_mode_p (layer); } -static void +void _cogl_material_layer_get_wrap_modes (CoglMaterialLayer *layer, CoglMaterialWrapModeInternal *wrap_mode_s, CoglMaterialWrapModeInternal *wrap_mode_t, @@ -3058,7 +3037,7 @@ _cogl_material_layers_equal (CoglMaterial *authority0, } /* Determine the mask of differences between two materials */ -static unsigned long +unsigned long _cogl_material_compare_differences (CoglMaterial *material0, CoglMaterial *material1) { @@ -4286,8 +4265,12 @@ _cogl_material_layer_free (CoglMaterialLayer *layer) */ for (i = 0; i < COGL_MATERIAL_N_BACKENDS; i++) { - if (layer->backend_priv[i] && backends[i]->free_layer_priv) - backends[i]->free_layer_priv (layer); + if (layer->backend_priv[i] && + _cogl_material_backends[i]->free_layer_priv) + { + const CoglMaterialBackend *backend = _cogl_material_backends[i]; + backend->free_layer_priv (layer); + } } if (layer->differences & COGL_MATERIAL_LAYER_STATE_TEXTURE) @@ -4895,7 +4878,7 @@ _cogl_material_layer_has_user_matrix (CoglMaterialLayer *layer) return _cogl_material_layer_get_parent (authority) ? TRUE : FALSE; } -static void +void _cogl_material_layer_get_filters (CoglMaterialLayer *layer, CoglMaterialFilter *min_filter, CoglMaterialFilter *mag_filter) @@ -5078,968 +5061,6 @@ cogl_material_set_point_size (CoglHandle handle, _cogl_material_point_size_equal); } -void -_cogl_gl_use_program_wrapper (GLuint program) -{ -#ifdef COGL_MATERIAL_BACKEND_GLSL - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - if (ctx->current_gl_program == program) - return; - - if (program) - { - GLenum gl_error; - - while ((gl_error = glGetError ()) != GL_NO_ERROR) - ; - glUseProgram (program); - if (glGetError () != GL_NO_ERROR) - { - GE (glUseProgram (0)); - ctx->current_gl_program = 0; - return; - } - } - else - GE (glUseProgram (0)); - - ctx->current_gl_program = program; -#endif -} - -static void -disable_glsl (void) -{ -#ifdef COGL_MATERIAL_BACKEND_GLSL - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_GLSL) - _cogl_gl_use_program_wrapper (0); -#endif -} - -static void -disable_arbfp (void) -{ -#ifdef COGL_MATERIAL_BACKEND_ARBFP - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_ARBFP) - GE (glDisable (GL_FRAGMENT_PROGRAM_ARB)); -#endif -} - -void -_cogl_use_program (CoglHandle program_handle, CoglMaterialProgramType type) -{ - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - switch (type) - { -#ifdef COGL_MATERIAL_BACKEND_GLSL - case COGL_MATERIAL_PROGRAM_TYPE_GLSL: - { - /* The GLES2 backend currently manages its own codegen for - * fixed function API fallbacks and manages its own shader - * state. */ -#ifndef HAVE_COGL_GLES2 - CoglProgram *program = - _cogl_program_pointer_from_handle (program_handle); - - _cogl_gl_use_program_wrapper (program->gl_handle); - disable_arbfp (); -#endif - - ctx->current_use_program_type = type; - break; - } -#else - case COGL_MATERIAL_PROGRAM_TYPE_GLSL: - g_warning ("Unexpected use of GLSL backend!"); - break; -#endif -#ifdef COGL_MATERIAL_BACKEND_ARBFP - case COGL_MATERIAL_PROGRAM_TYPE_ARBFP: - - /* _cogl_gl_use_program_wrapper can be called by cogl-program.c - * so we can't bailout without making sure we glUseProgram (0) - * first. */ - disable_glsl (); - - if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_ARBFP) - break; - - GE (glEnable (GL_FRAGMENT_PROGRAM_ARB)); - - ctx->current_use_program_type = type; - break; -#else - case COGL_MATERIAL_PROGRAM_TYPE_ARBFP: - g_warning ("Unexpected use of GLSL backend!"); - break; -#endif -#ifdef COGL_MATERIAL_BACKEND_FIXED - case COGL_MATERIAL_PROGRAM_TYPE_FIXED: - - /* _cogl_gl_use_program_wrapper can be called by cogl-program.c - * so we can't bailout without making sure we glUseProgram (0) - * first. */ - disable_glsl (); - - if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_FIXED) - break; - - disable_arbfp (); - - ctx->current_use_program_type = type; -#endif - } -} - -#if defined (COGL_MATERIAL_BACKEND_GLSL) || \ - defined (COGL_MATERIAL_BACKEND_ARBFP) -int -_cogl_get_max_texture_image_units (void) -{ - _COGL_GET_CONTEXT (ctx, 0); - - /* This function is called quite often so we cache the value to - avoid too many GL calls */ - if (G_UNLIKELY (ctx->max_texture_image_units == -1)) - { - ctx->max_texture_image_units = 1; - GE (glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, - &ctx->max_texture_image_units)); - } - - return ctx->max_texture_image_units; -} -#endif - -static void -_cogl_material_layer_get_texture_info (CoglMaterialLayer *layer, - CoglHandle *texture, - GLuint *gl_texture, - GLuint *gl_target) -{ - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - *texture = layer->texture; - if (G_UNLIKELY (*texture == COGL_INVALID_HANDLE)) - *texture = ctx->default_gl_texture_2d_tex; - if (layer->texture_overridden) - { - *gl_texture = layer->slice_gl_texture; - *gl_target = layer->slice_gl_target; - } - else - cogl_texture_get_gl_texture (*texture, gl_texture, gl_target); -} - -#ifndef HAVE_COGL_GLES - -static gboolean -blend_factor_uses_constant (GLenum blend_factor) -{ - return (blend_factor == GL_CONSTANT_COLOR || - blend_factor == GL_ONE_MINUS_CONSTANT_COLOR || - blend_factor == GL_CONSTANT_ALPHA || - blend_factor == GL_ONE_MINUS_CONSTANT_ALPHA); -} - -#endif - -static void -flush_depth_state (CoglMaterialDepthState *depth_state) -{ - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - if (ctx->depth_test_function_cache != depth_state->depth_test_function) - { - GE (glDepthFunc (depth_state->depth_test_function)); - ctx->depth_test_function_cache = depth_state->depth_test_function; - } - - if (ctx->depth_writing_enabled_cache != depth_state->depth_writing_enabled) - { - GE (glDepthMask (depth_state->depth_writing_enabled ? - GL_TRUE : GL_FALSE)); - ctx->depth_writing_enabled_cache = depth_state->depth_writing_enabled; - } - -#ifndef COGL_HAS_GLES - if (ctx->depth_range_near_cache != depth_state->depth_range_near || - ctx->depth_range_far_cache != depth_state->depth_range_far) - { -#ifdef COGL_HAS_GLES2 - GE (glDepthRangef (depth_state->depth_range_near, - depth_state->depth_range_far)); -#else - GE (glDepthRange (depth_state->depth_range_near, - depth_state->depth_range_far)); -#endif - ctx->depth_range_near_cache = depth_state->depth_range_near; - ctx->depth_range_far_cache = depth_state->depth_range_far; - } -#endif /* COGL_HAS_GLES */ -} - -static void -_cogl_material_flush_color_blend_alpha_depth_state ( - CoglMaterial *material, - unsigned long materials_difference, - gboolean skip_gl_color) -{ - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - if (!skip_gl_color) - { - if ((materials_difference & COGL_MATERIAL_STATE_COLOR) || - /* Assume if we were previously told to skip the color, then - * the current color needs updating... */ - ctx->current_material_skip_gl_color) - { - CoglMaterial *authority = - _cogl_material_get_authority (material, COGL_MATERIAL_STATE_COLOR); - GE (glColor4ub (cogl_color_get_red_byte (&authority->color), - cogl_color_get_green_byte (&authority->color), - cogl_color_get_blue_byte (&authority->color), - cogl_color_get_alpha_byte (&authority->color))); - } - } - - if (materials_difference & COGL_MATERIAL_STATE_LIGHTING) - { - CoglMaterial *authority = - _cogl_material_get_authority (material, COGL_MATERIAL_STATE_LIGHTING); - CoglMaterialLightingState *lighting_state = - &authority->big_state->lighting_state; - - /* FIXME - we only need to set these if lighting is enabled... */ - GLfloat shininess = lighting_state->shininess * 128.0f; - - GE (glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, lighting_state->ambient)); - GE (glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, lighting_state->diffuse)); - GE (glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, lighting_state->specular)); - GE (glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, lighting_state->emission)); - GE (glMaterialfv (GL_FRONT_AND_BACK, GL_SHININESS, &shininess)); - } - - if (materials_difference & COGL_MATERIAL_STATE_BLEND) - { - CoglMaterial *authority = - _cogl_material_get_authority (material, COGL_MATERIAL_STATE_BLEND); - CoglMaterialBlendState *blend_state = - &authority->big_state->blend_state; - -#if defined (HAVE_COGL_GLES2) - gboolean have_blend_equation_seperate = TRUE; - gboolean have_blend_func_separate = TRUE; -#elif defined (HAVE_COGL_GL) - gboolean have_blend_equation_seperate = FALSE; - gboolean have_blend_func_separate = FALSE; - if (ctx->drv.pf_glBlendEquationSeparate) /* Only GL 2.0 + */ - have_blend_equation_seperate = TRUE; - if (ctx->drv.pf_glBlendFuncSeparate) /* Only GL 1.4 + */ - have_blend_func_separate = TRUE; -#endif - -#ifndef HAVE_COGL_GLES /* GLES 1 only has glBlendFunc */ - if (blend_factor_uses_constant (blend_state->blend_src_factor_rgb) || - blend_factor_uses_constant (blend_state->blend_src_factor_alpha) || - blend_factor_uses_constant (blend_state->blend_dst_factor_rgb) || - blend_factor_uses_constant (blend_state->blend_dst_factor_alpha)) - { - float red = - cogl_color_get_red_float (&blend_state->blend_constant); - float green = - cogl_color_get_green_float (&blend_state->blend_constant); - float blue = - cogl_color_get_blue_float (&blend_state->blend_constant); - float alpha = - cogl_color_get_alpha_float (&blend_state->blend_constant); - - - GE (glBlendColor (red, green, blue, alpha)); - } - - if (have_blend_equation_seperate && - blend_state->blend_equation_rgb != blend_state->blend_equation_alpha) - GE (glBlendEquationSeparate (blend_state->blend_equation_rgb, - blend_state->blend_equation_alpha)); - else - GE (glBlendEquation (blend_state->blend_equation_rgb)); - - if (have_blend_func_separate && - (blend_state->blend_src_factor_rgb != blend_state->blend_src_factor_alpha || - (blend_state->blend_src_factor_rgb != - blend_state->blend_src_factor_alpha))) - GE (glBlendFuncSeparate (blend_state->blend_src_factor_rgb, - blend_state->blend_dst_factor_rgb, - blend_state->blend_src_factor_alpha, - blend_state->blend_dst_factor_alpha)); - else -#endif - GE (glBlendFunc (blend_state->blend_src_factor_rgb, - blend_state->blend_dst_factor_rgb)); - } - - if (materials_difference & COGL_MATERIAL_STATE_ALPHA_FUNC) - { - CoglMaterial *authority = - _cogl_material_get_authority (material, COGL_MATERIAL_STATE_ALPHA_FUNC); - CoglMaterialAlphaFuncState *alpha_state = - &authority->big_state->alpha_state; - - /* NB: Currently the Cogl defines are compatible with the GL ones: */ - GE (glAlphaFunc (alpha_state->alpha_func, - alpha_state->alpha_func_reference)); - } - - if (materials_difference & COGL_MATERIAL_STATE_DEPTH) - { - CoglMaterial *authority = - _cogl_material_get_authority (material, COGL_MATERIAL_STATE_DEPTH); - CoglMaterialDepthState *depth_state = &authority->big_state->depth_state; - - if (depth_state->depth_test_enabled) - { - if (ctx->depth_test_enabled_cache != TRUE) - { - GE (glEnable (GL_DEPTH_TEST)); - ctx->depth_test_enabled_cache = depth_state->depth_test_enabled; - } - flush_depth_state (depth_state); - } - else if (ctx->depth_test_enabled_cache != FALSE) - { - GE (glDisable (GL_DEPTH_TEST)); - ctx->depth_test_enabled_cache = depth_state->depth_test_enabled; - } - } - - if (materials_difference & COGL_MATERIAL_STATE_POINT_SIZE) - { - CoglMaterial *authority = - _cogl_material_get_authority (material, COGL_MATERIAL_STATE_POINT_SIZE); - - if (ctx->point_size_cache != authority->big_state->point_size) - { - GE( glPointSize (authority->big_state->point_size) ); - ctx->point_size_cache = authority->big_state->point_size; - } - } - - if (material->real_blend_enable != ctx->gl_blend_enable_cache) - { - if (material->real_blend_enable) - GE (glEnable (GL_BLEND)); - else - GE (glDisable (GL_BLEND)); - /* XXX: we shouldn't update any other blend state if blending - * is disabled! */ - ctx->gl_blend_enable_cache = material->real_blend_enable; - } -} - -static int -get_max_activateable_texture_units (void) -{ - _COGL_GET_CONTEXT (ctx, 0); - - if (G_UNLIKELY (ctx->max_activateable_texture_units == -1)) - { -#ifdef HAVE_COGL_GL - GLint max_tex_coords; - GLint max_combined_tex_units; - GE (glGetIntegerv (GL_MAX_TEXTURE_COORDS, &max_tex_coords)); - GE (glGetIntegerv (GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, - &max_combined_tex_units)); - ctx->max_activateable_texture_units = - MAX (max_tex_coords - 1, max_combined_tex_units); -#else - GE (glGetIntegerv (GL_MAX_TEXTURE_UNITS, - &ctx->max_activateable_texture_units)); -#endif - } - - return ctx->max_activateable_texture_units; -} - -typedef struct -{ - int i; - unsigned long *layer_differences; -} CoglMaterialFlushLayerState; - -static gboolean -flush_layers_common_gl_state_cb (CoglMaterialLayer *layer, void *user_data) -{ - CoglMaterialFlushLayerState *flush_state = user_data; - int unit_index = flush_state->i; - CoglTextureUnit *unit = _cogl_get_texture_unit (unit_index); - unsigned long layers_difference = - flush_state->layer_differences[unit_index]; - - /* There may not be enough texture units so we can bail out if - * that's the case... - */ - if (G_UNLIKELY (unit_index >= get_max_activateable_texture_units ())) - { - static gboolean shown_warning = FALSE; - - if (!shown_warning) - { - g_warning ("Your hardware does not have enough texture units" - "to handle this many texture layers"); - shown_warning = TRUE; - } - return FALSE; - } - - if (layers_difference & COGL_MATERIAL_LAYER_STATE_TEXTURE) - { - CoglMaterialLayer *authority = - _cogl_material_layer_get_authority (layer, - COGL_MATERIAL_LAYER_STATE_TEXTURE); - CoglHandle texture = NULL; - GLuint gl_texture; - GLenum gl_target; - - _cogl_material_layer_get_texture_info (authority, - &texture, - &gl_texture, - &gl_target); - - _cogl_set_active_texture_unit (unit_index); - - /* NB: There are several Cogl components and some code in - * Clutter that will temporarily bind arbitrary GL textures to - * query and modify texture object parameters. If you look at - * _cogl_bind_gl_texture_transient() you can see we make sure - * that such code always binds to texture unit 1 which means we - * can't rely on the unit->gl_texture state if unit->index == 1. - * - * Because texture unit 1 is a bit special we actually defer any - * necessary glBindTexture for it until the end of - * _cogl_material_flush_gl_state(). - * - * NB: we get notified whenever glDeleteTextures is used (see - * _cogl_delete_gl_texture()) where we invalidate - * unit->gl_texture references to deleted textures so it's safe - * to compare unit->gl_texture with gl_texture. (Without the - * hook it would be possible to delete a GL texture and create a - * new one with the same name and comparing unit->gl_texture and - * gl_texture wouldn't detect that.) - * - * NB: for foreign textures we don't know how the deletion of - * the GL texture objects correspond to the deletion of the - * CoglTextures so if there was previously a foreign texture - * associated with the texture unit then we can't assume that we - * aren't seeing a recycled texture name so we have to bind. - */ - if (unit->gl_texture != gl_texture || unit->is_foreign) - { - if (unit_index != 1) - GE (glBindTexture (gl_target, gl_texture)); - unit->gl_texture = gl_texture; - } - - unit->is_foreign = _cogl_texture_is_foreign (texture); - - /* Disable the previous target if it was different and it's - * still enabled */ - if (unit->enabled && unit->current_gl_target != gl_target) - GE (glDisable (unit->current_gl_target)); - - if (!G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_TEXTURING) && - (!unit->enabled || unit->current_gl_target != gl_target)) - { - GE (glEnable (gl_target)); - unit->enabled = TRUE; - unit->current_gl_target = gl_target; - } - - /* The texture_storage_changed boolean indicates if the - * CoglTexture's underlying GL texture storage has changed since - * it was flushed to the texture unit. We've just flushed the - * latest state so we can reset this. */ - unit->texture_storage_changed = FALSE; - } - else - { - /* Even though there may be no difference between the last flushed - * texture state and the current layers texture state it may be that the - * texture unit has been disabled for some time so we need to assert that - * it's enabled now. - */ - if (!G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_TEXTURING) && - !unit->enabled) - { - GE (glEnable (unit->current_gl_target)); - unit->enabled = TRUE; - } - } - - if (layers_difference & COGL_MATERIAL_LAYER_STATE_USER_MATRIX) - { - CoglMaterialLayerState state = COGL_MATERIAL_LAYER_STATE_USER_MATRIX; - CoglMaterialLayer *authority = - _cogl_material_layer_get_authority (layer, state); - - _cogl_matrix_stack_set (unit->matrix_stack, - &authority->big_state->matrix); - - _cogl_matrix_stack_flush_to_gl (unit->matrix_stack, COGL_MATRIX_TEXTURE); - } - - if (layers_difference & COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS) - { - CoglMaterialState change = COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS; - CoglMaterialLayer *authority = - _cogl_material_layer_get_authority (layer, change); - CoglMaterialLayerBigState *big_state = authority->big_state; - - _cogl_set_active_texture_unit (unit_index); - - GE (glTexEnvi (GL_POINT_SPRITE, GL_COORD_REPLACE, - big_state->point_sprite_coords)); - } - - cogl_handle_ref (layer); - if (unit->layer != COGL_INVALID_HANDLE) - cogl_handle_unref (unit->layer); - - unit->layer = layer; - unit->layer_changes_since_flush = 0; - - flush_state->i++; - - return TRUE; -} - -static void -_cogl_material_flush_common_gl_state (CoglMaterial *material, - unsigned long materials_difference, - unsigned long *layer_differences, - gboolean skip_gl_color) -{ - CoglMaterialFlushLayerState state; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - _cogl_material_flush_color_blend_alpha_depth_state (material, - materials_difference, - skip_gl_color); - - state.i = 0; - state.layer_differences = layer_differences; - _cogl_material_foreach_layer (material, - flush_layers_common_gl_state_cb, - &state); - - /* Disable additional texture units that may have previously been in use.. */ - for (; state.i < ctx->texture_units->len; state.i++) - _cogl_disable_texture_unit (state.i); -} - -/* Re-assert the layer's wrap modes on the given CoglTexture. - * - * Note: we don't simply forward the wrap modes to layer->texture - * since the actual texture being used may have been overridden. - */ -static void -_cogl_material_layer_forward_wrap_modes (CoglMaterialLayer *layer, - CoglHandle texture) -{ - CoglMaterialWrapModeInternal wrap_mode_s, wrap_mode_t, wrap_mode_p; - GLenum gl_wrap_mode_s, gl_wrap_mode_t, gl_wrap_mode_p; - - if (texture == COGL_INVALID_HANDLE) - return; - - _cogl_material_layer_get_wrap_modes (layer, - &wrap_mode_s, - &wrap_mode_t, - &wrap_mode_p); - - /* Update the wrap mode on the texture object. The texture backend - should cache the value so that it will be a no-op if the object - already has the same wrap mode set. The backend is best placed to - do this because it knows how many of the coordinates will - actually be used (ie, a 1D texture only cares about the 's' - coordinate but a 3D texture would use all three). GL uses the - wrap mode as part of the texture object state but we are - pretending it's part of the per-layer environment state. This - will break if the application tries to use different modes in - different layers using the same texture. */ - - if (wrap_mode_s == COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC) - gl_wrap_mode_s = GL_CLAMP_TO_EDGE; - else - gl_wrap_mode_s = wrap_mode_s; - - if (wrap_mode_t == COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC) - gl_wrap_mode_t = GL_CLAMP_TO_EDGE; - else - gl_wrap_mode_t = wrap_mode_t; - - if (wrap_mode_p == COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC) - gl_wrap_mode_p = GL_CLAMP_TO_EDGE; - else - gl_wrap_mode_p = wrap_mode_p; - - _cogl_texture_set_wrap_mode_parameters (texture, - gl_wrap_mode_s, - gl_wrap_mode_t, - gl_wrap_mode_p); -} - -/* OpenGL associates the min/mag filters and repeat modes with the - * texture object not the texture unit so we always have to re-assert - * the filter and repeat modes whenever we use a texture since it may - * be referenced by multiple materials with different modes. - * - * XXX: GL_ARB_sampler_objects fixes this in OpenGL so we should - * eventually look at using this extension when available. - */ -static void -foreach_texture_unit_update_filter_and_wrap_modes (void) -{ - int i; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - for (i = 0; i < ctx->texture_units->len; i++) - { - CoglTextureUnit *unit = - &g_array_index (ctx->texture_units, CoglTextureUnit, i); - - if (!unit->enabled) - break; - - if (unit->layer) - { - CoglHandle texture = _cogl_material_layer_get_texture (unit->layer); - CoglMaterialFilter min; - CoglMaterialFilter mag; - - _cogl_material_layer_get_filters (unit->layer, &min, &mag); - _cogl_texture_set_filters (texture, min, mag); - - _cogl_material_layer_forward_wrap_modes (unit->layer, texture); - } - } -} - -typedef struct -{ - int i; - unsigned long *layer_differences; -} CoglMaterialCompareLayersState; - -static gboolean -compare_layer_differences_cb (CoglMaterialLayer *layer, void *user_data) -{ - CoglMaterialCompareLayersState *state = user_data; - CoglTextureUnit *unit = _cogl_get_texture_unit (state->i); - - if (unit->layer == layer) - state->layer_differences[state->i] = unit->layer_changes_since_flush; - else if (unit->layer) - { - state->layer_differences[state->i] = unit->layer_changes_since_flush; - state->layer_differences[state->i] |= - _cogl_material_layer_compare_differences (layer, unit->layer); - } - else - state->layer_differences[state->i] = COGL_MATERIAL_LAYER_STATE_ALL_SPARSE; - - /* XXX: There is always a possibility that a CoglTexture's - * underlying GL texture storage has been changed since it was last - * bound to a texture unit which is why we have a callback into - * _cogl_material_texture_storage_change_notify whenever a textures - * underlying GL texture storage changes which will set the - * unit->texture_intern_changed flag. If we see that's been set here - * then we force an update of the texture state... - */ - if (unit->texture_storage_changed) - state->layer_differences[state->i] |= COGL_MATERIAL_LAYER_STATE_TEXTURE; - - state->i++; - - return TRUE; -} - -typedef struct -{ - const CoglMaterialBackend *backend; - CoglMaterial *material; - unsigned long *layer_differences; - gboolean error_adding_layer; - gboolean added_layer; -} CoglMaterialBackendAddLayerState; - - -static gboolean -backend_add_layer_cb (CoglMaterialLayer *layer, - void *user_data) -{ - CoglMaterialBackendAddLayerState *state = user_data; - const CoglMaterialBackend *backend = state->backend; - CoglMaterial *material = state->material; - int unit_index = _cogl_material_layer_get_unit_index (layer); - CoglTextureUnit *unit = _cogl_get_texture_unit (unit_index); - - _COGL_GET_CONTEXT (ctx, FALSE); - - /* NB: We don't support the random disabling of texture - * units, so as soon as we hit a disabled unit we know all - * subsequent units are also disabled */ - if (!unit->enabled) - return FALSE; - - if (G_UNLIKELY (unit_index >= backend->get_max_texture_units ())) - { - int j; - for (j = unit_index; j < ctx->texture_units->len; j++) - _cogl_disable_texture_unit (j); - /* TODO: although this isn't considered an error that - * warrants falling back to a different backend we - * should print a warning here. */ - return FALSE; - } - - /* Either generate per layer code snippets or setup the - * fixed function glTexEnv for each layer... */ - if (G_LIKELY (backend->add_layer (material, - layer, - state->layer_differences[unit_index]))) - state->added_layer = TRUE; - else - { - state->error_adding_layer = TRUE; - return FALSE; - } - - return TRUE; -} - -/* - * _cogl_material_flush_gl_state: - * - * Details of override options: - * ->fallback_mask: is a bitmask of the material layers that need to be - * replaced with the default, fallback textures. The fallback textures are - * fully transparent textures so they hopefully wont contribute to the - * texture combining. - * - * The intention of fallbacks is to try and preserve - * the number of layers the user is expecting so that texture coordinates - * they gave will mostly still correspond to the textures they intended, and - * have a fighting chance of looking close to their originally intended - * result. - * - * ->disable_mask: is a bitmask of the material layers that will simply have - * texturing disabled. It's only really intended for disabling all layers - * > X; i.e. we'd expect to see a contiguous run of 0 starting from the LSB - * and at some point the remaining bits flip to 1. It might work to disable - * arbitrary layers; though I'm not sure a.t.m how OpenGL would take to - * that. - * - * The intention of the disable_mask is for emitting geometry when the user - * hasn't supplied enough texture coordinates for all the layers and it's - * not possible to auto generate default texture coordinates for those - * layers. - * - * ->layer0_override_texture: forcibly tells us to bind this GL texture name for - * layer 0 instead of plucking the gl_texture from the CoglTexture of layer - * 0. - * - * The intention of this is for any primitives that supports sliced textures. - * The code will can iterate each of the slices and re-flush the material - * forcing the GL texture of each slice in turn. - * - * ->wrap_mode_overrides: overrides the wrap modes set on each - * layer. This is used to implement the automatic wrap mode. - * - * XXX: It might also help if we could specify a texture matrix for code - * dealing with slicing that would be multiplied with the users own matrix. - * - * Normaly texture coords in the range [0, 1] refer to the extents of the - * texture, but when your GL texture represents a slice of the real texture - * (from the users POV) then a texture matrix would be a neat way of - * transforming the mapping for each slice. - * - * Currently for textured rectangles we manually calculate the texture - * coords for each slice based on the users given coords, but this solution - * isn't ideal, and can't be used with CoglVertexBuffers. - */ -void -_cogl_material_flush_gl_state (CoglMaterial *material, - gboolean skip_gl_color) -{ - unsigned long materials_difference; - int n_layers; - unsigned long *layer_differences = NULL; - int i; - CoglTextureUnit *unit1; - - COGL_STATIC_TIMER (material_flush_timer, - "Mainloop", /* parent */ - "Material Flush", - "The time spent flushing material state", - 0 /* no application private data */); - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - COGL_TIMER_START (_cogl_uprof_context, material_flush_timer); - - if (ctx->current_material == material) - materials_difference = ctx->current_material_changes_since_flush; - else if (ctx->current_material) - { - materials_difference = ctx->current_material_changes_since_flush; - materials_difference |= - _cogl_material_compare_differences (ctx->current_material, - material); - } - else - materials_difference = COGL_MATERIAL_STATE_ALL_SPARSE; - - /* Get a layer_differences mask for each layer to be flushed */ - n_layers = cogl_material_get_n_layers (material); - if (n_layers) - { - CoglMaterialCompareLayersState state; - layer_differences = g_alloca (sizeof (unsigned long *) * n_layers); - memset (layer_differences, 0, sizeof (layer_differences)); - state.i = 0; - state.layer_differences = layer_differences; - _cogl_material_foreach_layer (material, - compare_layer_differences_cb, - &state); - } - - /* First flush everything that's the same regardless of which - * material backend is being used... - * - * 1) top level state: - * glColor (or skip if a vertex attribute is being used for color) - * blend state - * alpha test state (except for GLES 2.0) - * - * 2) then foreach layer: - * determine gl_target/gl_texture - * bind texture - * enable/disable target - * flush user matrix - * - * Note: After _cogl_material_flush_common_gl_state you can expect - * all state of the layers corresponding texture unit to be - * updated. - */ - _cogl_material_flush_common_gl_state (material, - materials_difference, - layer_differences, - skip_gl_color); - - /* Now flush the fragment processing state according to the current - * fragment processing backend. - * - * Note: Some of the backends may not support the current material - * configuration and in that case it will report an error and we - * will fallback to a different backend. - * - * NB: if material->backend != COGL_MATERIAL_BACKEND_UNDEFINED then - * we have previously managed to successfully flush this material - * with the given backend so we will simply use that to avoid - * fallback code paths. - */ - - if (material->backend == COGL_MATERIAL_BACKEND_UNDEFINED) - _cogl_material_set_backend (material, COGL_MATERIAL_BACKEND_DEFAULT); - - for (i = material->backend; - i < G_N_ELEMENTS (backends); - i++, _cogl_material_set_backend (material, i)) - { - const CoglMaterialBackend *backend = backends[i]; - CoglMaterialBackendAddLayerState state; - - /* E.g. For backends generating code they can setup their - * scratch buffers here... */ - if (G_UNLIKELY (!backend->start (material, - n_layers, - materials_difference))) - continue; - - state.backend = backend; - state.material = material; - state.layer_differences = layer_differences; - state.error_adding_layer = FALSE; - state.added_layer = FALSE; - _cogl_material_foreach_layer (material, - backend_add_layer_cb, - &state); - - if (G_UNLIKELY (state.error_adding_layer)) - continue; - - if (!state.added_layer && - backend->passthrough && - G_UNLIKELY (!backend->passthrough (material))) - continue; - - /* For backends generating code they may compile and link their - * programs here, update any uniforms and tell OpenGL to use - * that program. - */ - if (G_UNLIKELY (!backend->end (material, materials_difference))) - continue; - - break; - } - - /* FIXME: This reference is actually resulting in lots of - * copy-on-write reparenting because one-shot materials end up - * living for longer than necessary and so any later modification of - * the parent will cause a copy-on-write. - * - * XXX: The issue should largely go away when we switch to using - * weak materials for overrides. - */ - cogl_object_ref (material); - if (ctx->current_material != NULL) - cogl_object_unref (ctx->current_material); - ctx->current_material = material; - ctx->current_material_changes_since_flush = 0; - ctx->current_material_skip_gl_color = skip_gl_color; - - /* Handle the fact that OpenGL associates texture filter and wrap - * modes with the texture objects not the texture units... */ - foreach_texture_unit_update_filter_and_wrap_modes (); - - /* If this material has more than one layer then we always need - * to make sure we rebind the texture for unit 1. - * - * NB: various components of Cogl may temporarily bind arbitrary - * textures to texture unit 1 so they can query and modify texture - * object parameters. cogl-material.c (See - * _cogl_bind_gl_texture_transient) - */ - unit1 = _cogl_get_texture_unit (1); - if (unit1->enabled && unit1->dirty_gl_texture) - { - _cogl_set_active_texture_unit (1); - GE (glBindTexture (unit1->current_gl_target, unit1->gl_texture)); - unit1->dirty_gl_texture = FALSE; - } - - COGL_TIMER_STOP (_cogl_uprof_context, material_flush_timer); -} - /* While a material is referenced by the Cogl journal we can not allow * modifications, so this gives us a mechanism to track journal * references separately */ diff --git a/cogl/cogl-path.c b/cogl/cogl-path.c index e035ccb4c..904b7ef65 100644 --- a/cogl/cogl-path.c +++ b/cogl/cogl-path.c @@ -31,6 +31,7 @@ #include "cogl-context.h" #include "cogl-journal-private.h" #include "cogl-material-private.h" +#include "cogl-material-opengl-private.h" #include "cogl-framebuffer-private.h" #include "cogl-path-private.h" #include "cogl-texture-private.h" diff --git a/cogl/cogl-primitives.c b/cogl/cogl-primitives.c index 708130a95..e87231eb2 100644 --- a/cogl/cogl-primitives.c +++ b/cogl/cogl-primitives.c @@ -31,6 +31,7 @@ #include "cogl-journal-private.h" #include "cogl-texture-private.h" #include "cogl-material-private.h" +#include "cogl-material-opengl-private.h" #include "cogl-vertex-buffer-private.h" #include "cogl-framebuffer-private.h" diff --git a/cogl/cogl-vertex-buffer.c b/cogl/cogl-vertex-buffer.c index 54e48822a..d91ddc09c 100644 --- a/cogl/cogl-vertex-buffer.c +++ b/cogl/cogl-vertex-buffer.c @@ -137,6 +137,7 @@ #include "cogl-vertex-buffer-private.h" #include "cogl-texture-private.h" #include "cogl-material-private.h" +#include "cogl-material-opengl-private.h" #include "cogl-primitives.h" #include "cogl-framebuffer-private.h" #include "cogl-journal-private.h" diff --git a/cogl/cogl.c b/cogl/cogl.c index 8aa110e0c..5640204bb 100644 --- a/cogl/cogl.c +++ b/cogl/cogl.c @@ -37,6 +37,7 @@ #include "cogl-util.h" #include "cogl-context.h" #include "cogl-material-private.h" +#include "cogl-material-opengl-private.h" #include "cogl-winsys.h" #include "cogl-framebuffer-private.h" #include "cogl-matrix-private.h" diff --git a/cogl/driver/gl/cogl-program.c b/cogl/driver/gl/cogl-program.c index 73b1fd7d0..a51e4cf22 100644 --- a/cogl/driver/gl/cogl-program.c +++ b/cogl/driver/gl/cogl-program.c @@ -32,6 +32,7 @@ #include "cogl-handle.h" #include "cogl-context.h" #include "cogl-journal-private.h" +#include "cogl-material-opengl-private.h" #include