mirror of
https://github.com/brl/mutter.git
synced 2024-11-26 18:11:05 -05:00
63206a208b
This merges the two implementations of CoglProgram for the GLES2 and GL backends into one. The implementation is more like the GLES2 version which would track the uniform values and delay sending them to GL. CoglProgram is now effectively just a GList of CoglShaders along with an array of stored uniform values. CoglProgram never actually creates a GL program, instead this is left up to the GLSL material backend. This is necessary on GLES2 where we may need to relink the user's program with different generated shaders depending on the other emulated fixed function state. It will also be necessary in the future GLSL backends for regular OpenGL. The GLSL and ARBfp material backends are now the ones that create and link the GL program from the list of shaders. The linked program is attached to the private material state so that it can be reused if the CoglProgram is used again with the same material. This does mean the program will get relinked if the shader is used with multiple materials. This will be particularly bad if the legacy cogl_program_use function is used because that effectively always makes one-shot materials. This problem will hopefully be alleviated if we make a hash table with a cache of generated programs. The cogl program would then need to become part of the hash lookup. Each CoglProgram now has an age counter which is incremented every time a shader is added. This is used by the material backends to detect when we need to create a new GL program for the user program. The internal _cogl_use_program function now takes a GL program handle rather than a CoglProgram. It no longer needs any special differences for GLES2. The GLES2 wrapper function now also uses this function to bind its generated shaders. The ARBfp shaders no longer store a copy of the program source but instead just directly create a program object when cogl_shader_source is called. This avoids having to reupload the source if the same shader is used in multiple materials. There are currently a few gross hacks to get the GLES2 backend to work with this. The problem is that the GLSL material backend is now generating a complete GL program but the GLES2 wrapper still needs to add its fixed function emulation shaders if the program doesn't provide either a vertex or fragment shader. There is a new function in the GLES2 wrapper called _cogl_gles2_use_program which replaces the previous cogl_program_use implementation. It extracts the GL shaders from the GL program object and creates a new GL program containing all of the shaders plus its fixed function emulation. This new program is returned to the GLSL material backend so that it can still flush the custom uniforms using it. The user_program is attached to the GLES2 settings struct as before but its stored using a GL program handle rather than a CoglProgram pointer. This hack will go away once the GLSL material backend replaces the GLES2 wrapper by generating the code itself. Under Mesa this currently generates some GL errors when glClear is called in test-cogl-shader-glsl. I think this is due to a bug in Mesa however. When the user program on the material is changed the GLSL backend gets notified and deletes the GL program that it linked from the user shaders. The program will still be bound in GL however. Leaving a deleted shader bound exposes a bug in Mesa's glClear implementation. More details are here: https://bugs.freedesktop.org/show_bug.cgi?id=31194
1201 lines
40 KiB
C
1201 lines
40 KiB
C
/*
|
|
* Cogl
|
|
*
|
|
* An object oriented GL/GLES Abstraction/Utility Layer
|
|
*
|
|
* 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
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* 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/>.
|
|
*
|
|
*
|
|
*
|
|
* Authors:
|
|
* Robert Bragg <robert@linux.intel.com>
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "cogl.h"
|
|
|
|
#include "cogl-debug.h"
|
|
#include "cogl-material-opengl-private.h"
|
|
#include "cogl-material-private.h"
|
|
#include "cogl-context.h"
|
|
#include "cogl-texture-private.h"
|
|
|
|
#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 <glib.h>
|
|
#include <string.h>
|
|
|
|
/*
|
|
* 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_)
|
|
{
|
|
unit->index = index_;
|
|
unit->enabled = FALSE;
|
|
unit->current_gl_target = 0;
|
|
unit->gl_texture = 0;
|
|
unit->is_foreign = FALSE;
|
|
unit->dirty_gl_texture = FALSE;
|
|
unit->matrix_stack = _cogl_matrix_stack_new ();
|
|
|
|
unit->layer = NULL;
|
|
unit->layer_changes_since_flush = 0;
|
|
unit->texture_storage_changed = FALSE;
|
|
}
|
|
|
|
static void
|
|
texture_unit_free (CoglTextureUnit *unit)
|
|
{
|
|
if (unit->layer)
|
|
cogl_object_unref (unit->layer);
|
|
_cogl_matrix_stack_destroy (unit->matrix_stack);
|
|
}
|
|
|
|
CoglTextureUnit *
|
|
_cogl_get_texture_unit (int index_)
|
|
{
|
|
_COGL_GET_CONTEXT (ctx, NULL);
|
|
|
|
if (ctx->texture_units->len < (index_ + 1))
|
|
{
|
|
int i;
|
|
int prev_len = ctx->texture_units->len;
|
|
ctx->texture_units = g_array_set_size (ctx->texture_units, index_ + 1);
|
|
for (i = prev_len; i <= index_; i++)
|
|
{
|
|
CoglTextureUnit *unit =
|
|
&g_array_index (ctx->texture_units, CoglTextureUnit, i);
|
|
|
|
texture_unit_init (unit, i);
|
|
}
|
|
}
|
|
|
|
return &g_array_index (ctx->texture_units, CoglTextureUnit, index_);
|
|
}
|
|
|
|
void
|
|
_cogl_destroy_texture_units (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);
|
|
texture_unit_free (unit);
|
|
}
|
|
g_array_free (ctx->texture_units, TRUE);
|
|
}
|
|
|
|
void
|
|
_cogl_set_active_texture_unit (int unit_index)
|
|
{
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
if (ctx->active_texture_unit != unit_index)
|
|
{
|
|
GE (glActiveTexture (GL_TEXTURE0 + unit_index));
|
|
ctx->active_texture_unit = unit_index;
|
|
}
|
|
}
|
|
|
|
void
|
|
_cogl_disable_texture_unit (int unit_index)
|
|
{
|
|
CoglTextureUnit *unit;
|
|
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
unit = &g_array_index (ctx->texture_units, CoglTextureUnit, unit_index);
|
|
|
|
if (unit->enabled)
|
|
{
|
|
_cogl_set_active_texture_unit (unit_index);
|
|
GE (glDisable (unit->current_gl_target));
|
|
unit->enabled = FALSE;
|
|
}
|
|
}
|
|
|
|
/* Note: _cogl_bind_gl_texture_transient conceptually has slightly
|
|
* different semantics to OpenGL's glBindTexture because Cogl never
|
|
* cares about tracking multiple textures bound to different targets
|
|
* on the same texture unit.
|
|
*
|
|
* glBindTexture lets you bind multiple textures to a single texture
|
|
* unit if they are bound to different targets. So it does something
|
|
* like:
|
|
* unit->current_texture[target] = texture;
|
|
*
|
|
* Cogl only lets you associate one texture with the currently active
|
|
* texture unit, so the target is basically a redundant parameter
|
|
* that's implicitly set on that texture.
|
|
*
|
|
* Technically this is just a thin wrapper around glBindTexture so
|
|
* actually it does have the GL semantics but it seems worth
|
|
* mentioning the conceptual difference in case anyone wonders why we
|
|
* don't associate the gl_texture with a gl_target in the
|
|
* CoglTextureUnit.
|
|
*/
|
|
void
|
|
_cogl_bind_gl_texture_transient (GLenum gl_target,
|
|
GLuint gl_texture,
|
|
gboolean is_foreign)
|
|
{
|
|
CoglTextureUnit *unit;
|
|
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
/* We choose to always make texture unit 1 active for transient
|
|
* binds so that in the common case where multitexturing isn't used
|
|
* we can simply ignore the state of this texture unit. Notably we
|
|
* didn't use a large texture unit (.e.g. (GL_MAX_TEXTURE_UNITS - 1)
|
|
* in case the driver doesn't have a sparse data structure for
|
|
* texture units.
|
|
*/
|
|
_cogl_set_active_texture_unit (1);
|
|
unit = _cogl_get_texture_unit (1);
|
|
|
|
/* NB: If we have previously bound a foreign texture to this texture
|
|
* unit we don't know if that texture has since been deleted and we
|
|
* are seeing the texture name recycled */
|
|
if (unit->gl_texture == gl_texture &&
|
|
!unit->dirty_gl_texture &&
|
|
!unit->is_foreign)
|
|
return;
|
|
|
|
GE (glBindTexture (gl_target, gl_texture));
|
|
|
|
unit->dirty_gl_texture = TRUE;
|
|
unit->is_foreign = is_foreign;
|
|
}
|
|
|
|
void
|
|
_cogl_delete_gl_texture (GLuint gl_texture)
|
|
{
|
|
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->gl_texture == gl_texture)
|
|
{
|
|
unit->gl_texture = 0;
|
|
unit->dirty_gl_texture = FALSE;
|
|
}
|
|
}
|
|
|
|
GE (glDeleteTextures (1, &gl_texture));
|
|
}
|
|
|
|
/* Whenever the underlying GL texture storage of a CoglTexture is
|
|
* changed (e.g. due to migration out of a texture atlas) then we are
|
|
* notified. This lets us ensure that we reflush that texture's state
|
|
* if it is reused again with the same texture unit.
|
|
*/
|
|
void
|
|
_cogl_material_texture_storage_change_notify (CoglHandle texture)
|
|
{
|
|
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->layer &&
|
|
_cogl_material_layer_get_texture (unit->layer) == texture)
|
|
unit->texture_storage_changed = TRUE;
|
|
|
|
/* NB: the texture may be bound to multiple texture units so
|
|
* we continue to check the rest */
|
|
}
|
|
}
|
|
|
|
void
|
|
_cogl_use_program (GLuint gl_program, CoglMaterialProgramType type)
|
|
{
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
/* If we're changing program type... */
|
|
if (type != ctx->current_use_program_type)
|
|
{
|
|
/* ... disable the old type */
|
|
switch (ctx->current_use_program_type)
|
|
{
|
|
case COGL_MATERIAL_PROGRAM_TYPE_GLSL:
|
|
GE( glUseProgram (0) );
|
|
ctx->current_gl_program = 0;
|
|
break;
|
|
|
|
case COGL_MATERIAL_PROGRAM_TYPE_ARBFP:
|
|
#ifdef HAVE_COGL_GL
|
|
GE( glDisable (GL_FRAGMENT_PROGRAM_ARB) );
|
|
#endif
|
|
break;
|
|
|
|
case COGL_MATERIAL_PROGRAM_TYPE_FIXED:
|
|
/* don't need to to anything */
|
|
break;
|
|
}
|
|
|
|
/* ... and enable the new type */
|
|
switch (type)
|
|
{
|
|
case COGL_MATERIAL_PROGRAM_TYPE_ARBFP:
|
|
#ifdef HAVE_COGL_GL
|
|
GE( glEnable (GL_FRAGMENT_PROGRAM_ARB) );
|
|
#endif
|
|
break;
|
|
|
|
case COGL_MATERIAL_PROGRAM_TYPE_GLSL:
|
|
case COGL_MATERIAL_PROGRAM_TYPE_FIXED:
|
|
/* don't need to to anything */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (type == COGL_MATERIAL_PROGRAM_TYPE_GLSL)
|
|
{
|
|
#ifdef COGL_MATERIAL_BACKEND_GLSL
|
|
|
|
if (ctx->current_gl_program != gl_program)
|
|
{
|
|
GLenum gl_error;
|
|
|
|
while ((gl_error = glGetError ()) != GL_NO_ERROR)
|
|
;
|
|
glUseProgram (gl_program);
|
|
if (glGetError () == GL_NO_ERROR)
|
|
ctx->current_gl_program = gl_program;
|
|
else
|
|
{
|
|
GE( glUseProgram (0) );
|
|
ctx->current_gl_program = 0;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
g_warning ("Unexpected use of GLSL backend!");
|
|
|
|
#endif /* COGL_MATERIAL_BACKEND_GLSL */
|
|
}
|
|
#ifndef COGL_MATERIAL_BACKEND_ARBFP
|
|
else if (type == COGL_MATERIAL_PROGRAM_TYPE_ARBFP)
|
|
g_warning ("Unexpected use of ARBFP backend!");
|
|
#endif /* COGL_MATERIAL_BACKEND_ARBFP */
|
|
|
|
ctx->current_use_program_type = type;
|
|
}
|
|
|
|
#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;
|
|
|
|
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,
|
|
&lighting_state->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)
|
|
unit->dirty_gl_texture = TRUE;
|
|
else
|
|
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_internal (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_internal (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_internal (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);
|
|
}
|
|
|