diff --git a/cogl/Makefile.am b/cogl/Makefile.am index f11a07d0c..1b5d85854 100644 --- a/cogl/Makefile.am +++ b/cogl/Makefile.am @@ -240,6 +240,8 @@ cogl_sources_c = \ $(srcdir)/cogl-pipeline-fragend-arbfp-private.h \ $(srcdir)/cogl-pipeline-fragend-fixed.c \ $(srcdir)/cogl-pipeline-fragend-fixed-private.h \ + $(srcdir)/cogl-pipeline-vertend-fixed.c \ + $(srcdir)/cogl-pipeline-vertend-fixed-private.h \ $(srcdir)/cogl-pipeline-progend-glsl.c \ $(srcdir)/cogl-pipeline-progend-glsl-private.h \ $(srcdir)/cogl-material-compat.c \ diff --git a/cogl/cogl-pipeline-opengl.c b/cogl/cogl-pipeline-opengl.c index 88f9fc1db..f417ba195 100644 --- a/cogl/cogl-pipeline-opengl.c +++ b/cogl/cogl-pipeline-opengl.c @@ -421,21 +421,6 @@ _cogl_pipeline_flush_color_blend_alpha_depth_state ( } } - if (pipelines_difference & COGL_PIPELINE_STATE_LIGHTING) - { - CoglPipeline *authority = - _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LIGHTING); - CoglPipelineLightingState *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 (pipelines_difference & COGL_PIPELINE_STATE_BLEND) { CoglPipeline *authority = @@ -678,18 +663,6 @@ flush_layers_common_gl_state_cb (CoglPipelineLayer *layer, void *user_data) unit->texture_storage_changed = FALSE; } - if (layers_difference & COGL_PIPELINE_LAYER_STATE_USER_MATRIX) - { - CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_USER_MATRIX; - CoglPipelineLayer *authority = - _cogl_pipeline_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); - } - /* Under GLES2 the fragment shader will use gl_PointCoord instead of replacing the texture coordinates */ #ifndef HAVE_COGL_GLES2 @@ -905,6 +878,42 @@ fragend_add_layer_cb (CoglPipelineLayer *layer, return TRUE; } +typedef struct +{ + const CoglPipelineVertend *vertend; + CoglPipeline *pipeline; + unsigned long *layer_differences; + gboolean error_adding_layer; + gboolean added_layer; +} CoglPipelineVertendAddLayerState; + + +static gboolean +vertend_add_layer_cb (CoglPipelineLayer *layer, + void *user_data) +{ + CoglPipelineVertendAddLayerState *state = user_data; + const CoglPipelineVertend *vertend = state->vertend; + CoglPipeline *pipeline = state->pipeline; + int unit_index = _cogl_pipeline_layer_get_unit_index (layer); + + _COGL_GET_CONTEXT (ctx, FALSE); + + /* Either enerate per layer code snippets or setup the + * fixed function matrix uniforms for each layer... */ + if (G_LIKELY (vertend->add_layer (pipeline, + layer, + state->layer_differences[unit_index]))) + state->added_layer = TRUE; + else + { + state->error_adding_layer = TRUE; + return FALSE; + } + + return TRUE; +} + /* * _cogl_pipeline_flush_gl_state: * @@ -1094,6 +1103,52 @@ _cogl_pipeline_flush_gl_state (CoglPipeline *pipeline, if (G_UNLIKELY (i >= G_N_ELEMENTS (_cogl_pipeline_fragends))) g_warning ("No usable pipeline fragment backend was found!"); + /* Now flush the vertex processing state according to the current + * vertex processing backend. + */ + + if (pipeline->vertend == COGL_PIPELINE_VERTEND_UNDEFINED) + _cogl_pipeline_set_vertend (pipeline, COGL_PIPELINE_VERTEND_DEFAULT); + + for (i = pipeline->vertend; + i < G_N_ELEMENTS (_cogl_pipeline_vertends); + i++, _cogl_pipeline_set_vertend (pipeline, i)) + { + const CoglPipelineVertend *vertend = _cogl_pipeline_vertends[i]; + CoglPipelineVertendAddLayerState state; + + /* E.g. For vertends generating code they can setup their + * scratch buffers here... */ + if (G_UNLIKELY (!vertend->start (pipeline, + n_layers, + pipelines_difference))) + continue; + + state.vertend = vertend; + state.pipeline = pipeline; + state.layer_differences = layer_differences; + state.error_adding_layer = FALSE; + state.added_layer = FALSE; + _cogl_pipeline_foreach_layer_internal (pipeline, + vertend_add_layer_cb, + &state); + + if (G_UNLIKELY (state.error_adding_layer)) + continue; + + /* For vertends generating code they may compile and link their + * programs here, update any uniforms and tell OpenGL to use + * that program. + */ + if (G_UNLIKELY (!vertend->end (pipeline, pipelines_difference))) + continue; + + break; + } + + if (G_UNLIKELY (i >= G_N_ELEMENTS (_cogl_pipeline_vertends))) + g_warning ("No usable pipeline vertex backend was found!"); + for (i = 0; i < COGL_PIPELINE_N_PROGENDS; i++) if (_cogl_pipeline_progends[i]->end) _cogl_pipeline_progends[i]->end (pipeline, pipelines_difference, diff --git a/cogl/cogl-pipeline-private.h b/cogl/cogl-pipeline-private.h index 32644934a..bd7738294 100644 --- a/cogl/cogl-pipeline-private.h +++ b/cogl/cogl-pipeline-private.h @@ -71,6 +71,13 @@ typedef struct _CoglPipelineLayer CoglPipelineLayer; #define COGL_PIPELINE_FRAGEND_DEFAULT 0 #define COGL_PIPELINE_FRAGEND_UNDEFINED 3 +#define COGL_PIPELINE_VERTEND_FIXED 0 + +#define COGL_PIPELINE_N_VERTENDS 1 + +#define COGL_PIPELINE_VERTEND_DEFAULT 0 +#define COGL_PIPELINE_VERTEND_UNDEFINED 3 + /* If we have either of the GLSL backends then we also need a GLSL progend to combine the shaders generated into a single program. Currently there is only one progend but if we ever add @@ -610,7 +617,7 @@ struct _CoglPipeline * state with the ancestors of other pipelines and those ancestors * could currently be associated with different backends. * - * Each set bit indicates if the correspondong ->backend_privs[] + * Each set bit indicates if the corresponding ->fragend_privs[] * entry is valid. */ unsigned int fragend_priv_set_mask:COGL_PIPELINE_N_FRAGENDS; @@ -648,6 +655,7 @@ struct _CoglPipeline * the pipeline and any private state the backend has associated * with the pipeline. */ unsigned int fragend:3; + unsigned int vertend:3; }; typedef struct _CoglPipelineFragend @@ -674,6 +682,25 @@ typedef struct _CoglPipelineFragend void (*free_priv) (CoglPipeline *pipeline); } CoglPipelineFragend; +typedef struct _CoglPipelineVertend +{ + gboolean (*start) (CoglPipeline *pipeline, + int n_layers, + unsigned long pipelines_difference); + gboolean (*add_layer) (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + unsigned long layers_difference); + gboolean (*end) (CoglPipeline *pipeline, + unsigned long pipelines_difference); + + void (*pipeline_pre_change_notify) (CoglPipeline *pipeline, + CoglPipelineState change, + const CoglColor *new_color); + void (*layer_pre_change_notify) (CoglPipeline *owner, + CoglPipelineLayer *layer, + CoglPipelineLayerState change); +} CoglPipelineVertend; + typedef struct { void (*end) (CoglPipeline *pipeline, @@ -696,6 +723,8 @@ typedef enum extern const CoglPipelineFragend * _cogl_pipeline_fragends[COGL_PIPELINE_N_FRAGENDS]; +extern const CoglPipelineVertend * +_cogl_pipeline_vertends[COGL_PIPELINE_N_VERTENDS]; extern const CoglPipelineProgend * _cogl_pipeline_progends[]; @@ -933,6 +962,9 @@ _cogl_pipeline_weak_copy (CoglPipeline *pipeline, void _cogl_pipeline_set_fragend (CoglPipeline *pipeline, int fragend); +void +_cogl_pipeline_set_vertend (CoglPipeline *pipeline, int vertend); + CoglPipeline * _cogl_pipeline_get_parent (CoglPipeline *pipeline); diff --git a/cogl/cogl-pipeline-vertend-fixed-private.h b/cogl/cogl-pipeline-vertend-fixed-private.h new file mode 100644 index 000000000..59bce34e9 --- /dev/null +++ b/cogl/cogl-pipeline-vertend-fixed-private.h @@ -0,0 +1,36 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 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 + * . + * + * + * + * Authors: + * Neil Roberts + */ + +#ifndef __COGL_PIPELINE_VERTEND_FIXED_PRIVATE_H +#define __COGL_PIPELINE_VERTEND_FIXED_PRIVATE_H + +#include "cogl-pipeline-private.h" + +extern const CoglPipelineVertend _cogl_pipeline_fixed_vertend; + +#endif /* __COGL_PIPELINE_VERTEND_FIXED_PRIVATE_H */ + diff --git a/cogl/cogl-pipeline-vertend-fixed.c b/cogl/cogl-pipeline-vertend-fixed.c new file mode 100644 index 000000000..8e5a85b9a --- /dev/null +++ b/cogl/cogl-pipeline-vertend-fixed.c @@ -0,0 +1,114 @@ +/* + * 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 + * . + * + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-pipeline-private.h" +#include "cogl-pipeline-opengl-private.h" + +#ifdef COGL_PIPELINE_VERTEND_FIXED + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-context.h" +#include "cogl-handle.h" + +const CoglPipelineVertend _cogl_pipeline_fixed_vertend; + +static gboolean +_cogl_pipeline_vertend_fixed_start (CoglPipeline *pipeline, + int n_layers, + unsigned long pipelines_difference) +{ + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_FIXED)) + return FALSE; + + return TRUE; +} + +static gboolean +_cogl_pipeline_vertend_fixed_add_layer (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + unsigned long layers_difference) +{ + int unit_index = _cogl_pipeline_layer_get_unit_index (layer); + CoglTextureUnit *unit = _cogl_get_texture_unit (unit_index); + + if (layers_difference & COGL_PIPELINE_LAYER_STATE_USER_MATRIX) + { + CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_USER_MATRIX; + CoglPipelineLayer *authority = + _cogl_pipeline_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); + } + + return TRUE; +} + +static gboolean +_cogl_pipeline_vertend_fixed_end (CoglPipeline *pipeline, + unsigned long pipelines_difference) +{ + if (pipelines_difference & COGL_PIPELINE_STATE_LIGHTING) + { + CoglPipeline *authority = + _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LIGHTING); + CoglPipelineLightingState *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)); + } + + return TRUE; +} + +const CoglPipelineVertend _cogl_pipeline_fixed_vertend = +{ + _cogl_pipeline_vertend_fixed_start, + _cogl_pipeline_vertend_fixed_add_layer, + _cogl_pipeline_vertend_fixed_end, + NULL, /* pipeline_change_notify */ + NULL /* layer_change_notify */ +}; + +#endif /* COGL_PIPELINE_VERTEND_FIXED */ + diff --git a/cogl/cogl-pipeline.c b/cogl/cogl-pipeline.c index 75208d7e2..c2e490251 100644 --- a/cogl/cogl-pipeline.c +++ b/cogl/cogl-pipeline.c @@ -64,6 +64,7 @@ static void recursively_free_layer_caches (CoglPipeline *pipeline); static gboolean _cogl_pipeline_is_weak (CoglPipeline *pipeline); const CoglPipelineFragend *_cogl_pipeline_fragends[COGL_PIPELINE_N_FRAGENDS]; +const CoglPipelineVertend *_cogl_pipeline_vertends[COGL_PIPELINE_N_VERTENDS]; /* The 'MAX' here is so that we don't define an empty array when there are no progends */ const CoglPipelineProgend * @@ -82,6 +83,10 @@ _cogl_pipeline_progends[MAX (COGL_PIPELINE_N_PROGENDS, 1)]; #include "cogl-pipeline-progend-glsl-private.h" #endif +#ifdef COGL_PIPELINE_VERTEND_FIXED +#include "cogl-pipeline-vertend-fixed-private.h" +#endif + COGL_OBJECT_DEFINE (Pipeline, pipeline); /* This type was made deprecated before the cogl_is_pipeline_layer function was ever exposed in the public headers so there's no need @@ -224,11 +229,17 @@ _cogl_pipeline_init_default_pipeline (void) &_cogl_pipeline_glsl_progend; #endif +#ifdef COGL_PIPELINE_VERTEND_FIXED + _cogl_pipeline_vertends[COGL_PIPELINE_VERTEND_FIXED] = + &_cogl_pipeline_fixed_vertend; +#endif + _cogl_pipeline_node_init (COGL_PIPELINE_NODE (pipeline)); pipeline->is_weak = FALSE; pipeline->journal_ref_count = 0; pipeline->fragend = COGL_PIPELINE_FRAGEND_UNDEFINED; + pipeline->vertend = COGL_PIPELINE_VERTEND_UNDEFINED; pipeline->differences = COGL_PIPELINE_STATE_ALL_SPARSE; pipeline->real_blend_enable = FALSE; @@ -356,9 +367,9 @@ _cogl_pipeline_set_parent (CoglPipeline *pipeline, if (pipeline->differences & COGL_PIPELINE_STATE_LAYERS) recursively_free_layer_caches (pipeline); - /* If the fragment processing backend is also caching state along - * with the pipeline that depends on the pipeline's ancestry then it - * may be notified here... + /* If the backends are also caching state along with the pipeline + * that depends on the pipeline's ancestry then it may be notified + * here... */ if (pipeline->fragend != COGL_PIPELINE_FRAGEND_UNDEFINED && _cogl_pipeline_fragends[pipeline->fragend]->pipeline_set_parent_notify) @@ -443,6 +454,8 @@ _cogl_pipeline_copy (CoglPipeline *src, gboolean is_weak) pipeline->fragend = src->fragend; pipeline->fragend_priv_set_mask = 0; + pipeline->vertend = src->vertend; + pipeline->has_static_breadcrumb = FALSE; pipeline->age = 0; @@ -953,6 +966,12 @@ _cogl_pipeline_set_fragend (CoglPipeline *pipeline, int fragend) pipeline->fragend = fragend; } +void +_cogl_pipeline_set_vertend (CoglPipeline *pipeline, int vertend) +{ + pipeline->vertend = vertend; +} + static void _cogl_pipeline_copy_differences (CoglPipeline *dest, CoglPipeline *src, @@ -1184,13 +1203,45 @@ _cogl_pipeline_pre_change_notify (CoglPipeline *pipeline, if (pipeline->fragend == COGL_PIPELINE_FRAGEND_FIXED) _cogl_pipeline_set_fragend (pipeline, COGL_PIPELINE_FRAGEND_UNDEFINED); #endif +#ifdef COGL_PIPELINE_VERTEND_FIXED + if (pipeline->vertend == COGL_PIPELINE_VERTEND_FIXED) + _cogl_pipeline_set_vertend (pipeline, COGL_PIPELINE_VERTEND_UNDEFINED); +#endif + /* To simplify things for the backends we are careful about how + * we report STATE_LAYERS changes. + * + * All STATE_LAYERS changes with the exception of ->n_layers + * will also result in layer_pre_change_notifications. For + * backends that perform code generation for fragment processing + * they typically need to understand the details of how layers + * get changed to determine if they need to repeat codegen. It + * doesn't help them to report a pipeline STATE_LAYERS change + * for all layer changes since it's so broad, they really need + * to wait for the layer change to be notified. What does help + * though is to report a STATE_LAYERS change for a change in + * ->n_layers because they typically do need to repeat codegen + * in that case. + * + * This just ensures backends only get a single pipeline or + * layer pre-change notification for any particular change. + */ if (pipeline->fragend != COGL_PIPELINE_FRAGEND_UNDEFINED && _cogl_pipeline_fragends[pipeline->fragend]->pipeline_pre_change_notify) { const CoglPipelineFragend *fragend = _cogl_pipeline_fragends[pipeline->fragend]; + if (!from_layer_change) + fragend->pipeline_pre_change_notify (pipeline, change, new_color); + } + + if (pipeline->vertend != COGL_PIPELINE_VERTEND_UNDEFINED && + _cogl_pipeline_vertends[pipeline->vertend]->pipeline_pre_change_notify) + { + const CoglPipelineVertend *vertend = + _cogl_pipeline_vertends[pipeline->vertend]; + /* To simplify things for the backends we are careful about how * we report STATE_LAYERS changes. * @@ -1210,7 +1261,7 @@ _cogl_pipeline_pre_change_notify (CoglPipeline *pipeline, * layer pre-change notification for any particular change. */ if (!from_layer_change) - fragend->pipeline_pre_change_notify (pipeline, change, new_color); + vertend->pipeline_pre_change_notify (pipeline, change, new_color); } /* Notify all of the progends */ @@ -1524,6 +1575,21 @@ _cogl_pipeline_fragend_layer_change_notify (CoglPipeline *owner, } } +static void +_cogl_pipeline_vertend_layer_change_notify (CoglPipeline *owner, + CoglPipelineLayer *layer, + CoglPipelineLayerState change) +{ + /* NB: The comment in fragend_layer_change_notify applies here too */ + if (owner->vertend != COGL_PIPELINE_VERTEND_UNDEFINED && + _cogl_pipeline_vertends[owner->vertend]->layer_pre_change_notify) + { + const CoglPipelineVertend *vertend = + _cogl_pipeline_vertends[owner->vertend]; + vertend->layer_pre_change_notify (owner, layer, change); + } +} + static void _cogl_pipeline_progend_layer_change_notify (CoglPipeline *owner, CoglPipelineLayer *layer, @@ -1697,6 +1763,7 @@ _cogl_pipeline_layer_pre_change_notify (CoglPipeline *required_owner, * dependant on this layer so it's ok to modify it. */ _cogl_pipeline_fragend_layer_change_notify (required_owner, layer, change); + _cogl_pipeline_vertend_layer_change_notify (required_owner, layer, change); _cogl_pipeline_progend_layer_change_notify (required_owner, layer, change); /* If the layer being changed is the same as the last layer we @@ -4356,7 +4423,10 @@ cogl_pipeline_set_user_program (CoglPipeline *pipeline, _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); if (program != COGL_INVALID_HANDLE) - _cogl_pipeline_set_fragend (pipeline, COGL_PIPELINE_FRAGEND_DEFAULT); + { + _cogl_pipeline_set_fragend (pipeline, COGL_PIPELINE_FRAGEND_DEFAULT); + _cogl_pipeline_set_vertend (pipeline, COGL_PIPELINE_VERTEND_DEFAULT); + } /* If we are the current authority see if we can revert to one of our * ancestors being the authority */