"Initial commit to Gerrit"
[profile/ivi/cogl.git] / cogl / cogl-pipeline-fragend-fixed.c
1 /*
2  * Cogl
3  *
4  * An object oriented GL/GLES Abstraction/Utility Layer
5  *
6  * Copyright (C) 2008,2009,2010 Intel Corporation.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see
20  * <http://www.gnu.org/licenses/>.
21  *
22  *
23  *
24  * Authors:
25  *   Robert Bragg <robert@linux.intel.com>
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include "cogl-context-private.h"
33 #include "cogl-pipeline-private.h"
34 #include "cogl-pipeline-state-private.h"
35 #include "cogl-pipeline-opengl-private.h"
36
37 #ifdef COGL_PIPELINE_FRAGEND_FIXED
38
39 #include "cogl-internal.h"
40 #include "cogl-context-private.h"
41 #include "cogl-handle.h"
42
43 #include "cogl-texture-private.h"
44 #include "cogl-blend-string.h"
45 #include "cogl-profile.h"
46 #include "cogl-program-private.h"
47
48 #include <glib.h>
49 #include <glib/gprintf.h>
50 #include <string.h>
51
52 #ifndef GL_TEXTURE_RECTANGLE_ARB
53 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
54 #endif
55
56 const CoglPipelineFragend _cogl_pipeline_fixed_fragend;
57
58 static void
59 _cogl_disable_texture_unit (int unit_index)
60 {
61   CoglTextureUnit *unit;
62
63   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
64
65   unit = &g_array_index (ctx->texture_units, CoglTextureUnit, unit_index);
66
67   if (unit->enabled_gl_target)
68     {
69       _cogl_set_active_texture_unit (unit_index);
70       GE (ctx, glDisable (unit->enabled_gl_target));
71       unit->enabled_gl_target = 0;
72     }
73 }
74
75 static int
76 get_max_texture_units (void)
77 {
78   _COGL_GET_CONTEXT (ctx, 0);
79
80   /* This function is called quite often so we cache the value to
81      avoid too many GL calls */
82   if (ctx->max_texture_units == -1)
83     {
84       ctx->max_texture_units = 1;
85       GE (ctx, glGetIntegerv (GL_MAX_TEXTURE_UNITS,
86                               &ctx->max_texture_units));
87     }
88
89   return ctx->max_texture_units;
90 }
91
92 static gboolean
93 _cogl_pipeline_fragend_fixed_start (CoglPipeline *pipeline,
94                                     int n_layers,
95                                     unsigned long pipelines_difference,
96                                     int n_tex_coord_attribs)
97 {
98   CoglHandle user_program;
99
100   _COGL_GET_CONTEXT (ctx, FALSE);
101
102   if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_FIXED)))
103     return FALSE;
104
105   if (ctx->driver == COGL_DRIVER_GLES2)
106     return FALSE;
107
108   /* Fragment snippets are only supported in the GLSL fragend */
109   if (_cogl_pipeline_has_fragment_snippets (pipeline))
110     return FALSE;
111
112   /* If there is a user program with a fragment shader then the
113      appropriate backend for that language should handle it. We can
114      still use the fixed fragment backend if the program only contains
115      a vertex shader */
116   user_program = cogl_pipeline_get_user_program (pipeline);
117   if (user_program != COGL_INVALID_HANDLE &&
118       _cogl_program_has_fragment_shader (user_program))
119     return FALSE;
120
121   _cogl_use_fragment_program (0, COGL_PIPELINE_PROGRAM_TYPE_FIXED);
122   return TRUE;
123 }
124
125 static void
126 translate_sources (CoglPipeline *pipeline,
127                    int n_sources,
128                    CoglPipelineCombineSource *source_in,
129                    GLenum *source_out)
130 {
131   int i;
132
133   /* The texture source numbers specified in the layer combine are the
134      layer numbers so we need to map these to unit indices */
135
136   for (i = 0; i < n_sources; i++)
137     switch (source_in[i])
138       {
139       case COGL_PIPELINE_COMBINE_SOURCE_TEXTURE:
140         source_out[i] = GL_TEXTURE;
141         break;
142
143       case COGL_PIPELINE_COMBINE_SOURCE_CONSTANT:
144         source_out[i] = GL_CONSTANT;
145         break;
146
147       case COGL_PIPELINE_COMBINE_SOURCE_PRIMARY_COLOR:
148         source_out[i] = GL_PRIMARY_COLOR;
149         break;
150
151       case COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS:
152         source_out[i] = GL_PREVIOUS;
153         break;
154
155       default:
156         {
157           int layer_num = source_in[i] - COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0;
158           CoglPipelineGetLayerFlags flags = COGL_PIPELINE_GET_LAYER_NO_CREATE;
159           CoglPipelineLayer *layer =
160             _cogl_pipeline_get_layer_with_flags (pipeline, layer_num, flags);
161
162           if (layer == NULL)
163             {
164               static gboolean warning_seen = FALSE;
165               if (!warning_seen)
166                 {
167                   g_warning ("The application is trying to use a texture "
168                              "combine with a layer number that does not exist");
169                   warning_seen = TRUE;
170                 }
171               source_out[i] = GL_PREVIOUS;
172             }
173           else
174             source_out[i] = (_cogl_pipeline_layer_get_unit_index (layer) +
175                              GL_TEXTURE0);
176         }
177       }
178 }
179
180 static gboolean
181 _cogl_pipeline_fragend_fixed_add_layer (CoglPipeline *pipeline,
182                                         CoglPipelineLayer *layer,
183                                         unsigned long layers_difference)
184 {
185   CoglTextureUnit *unit =
186     _cogl_get_texture_unit (_cogl_pipeline_layer_get_unit_index (layer));
187   int unit_index = unit->index;
188   int n_rgb_func_args;
189   int n_alpha_func_args;
190
191   _COGL_GET_CONTEXT (ctx, FALSE);
192
193   /* XXX: Beware that since we are changing the active texture unit we
194    * must make sure we don't call into other Cogl components that may
195    * temporarily bind texture objects to query/modify parameters since
196    * they will end up binding texture unit 1. See
197    * _cogl_bind_gl_texture_transient for more details.
198    */
199   _cogl_set_active_texture_unit (unit_index);
200
201   if (G_UNLIKELY (unit_index >= get_max_texture_units ()))
202     {
203       _cogl_disable_texture_unit (unit_index);
204       /* TODO: although this isn't considered an error that
205        * warrants falling back to a different backend we
206        * should print a warning here. */
207       return TRUE;
208     }
209
210   /* Handle enabling or disabling the right texture type */
211   if (layers_difference & COGL_PIPELINE_LAYER_STATE_TEXTURE_TYPE)
212     {
213       CoglTextureType texture_type =
214         _cogl_pipeline_layer_get_texture_type (layer);
215       GLenum gl_target;
216
217       switch (texture_type)
218         {
219         case COGL_TEXTURE_TYPE_2D:
220           gl_target = GL_TEXTURE_2D;
221           break;
222
223         case COGL_TEXTURE_TYPE_3D:
224           gl_target = GL_TEXTURE_3D;
225           break;
226
227         case COGL_TEXTURE_TYPE_RECTANGLE:
228           gl_target = GL_TEXTURE_RECTANGLE_ARB;
229           break;
230         }
231
232       _cogl_set_active_texture_unit (unit_index);
233
234       /* The common GL code handles binding the right texture so we
235          just need to handle enabling and disabling it */
236
237       if (unit->enabled_gl_target != gl_target)
238         {
239           /* Disable the previous target if it's still enabled */
240           if (unit->enabled_gl_target)
241             GE (ctx, glDisable (unit->enabled_gl_target));
242
243           /* Enable the new target */
244           if (!G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_TEXTURING)))
245             {
246               GE (ctx, glEnable (gl_target));
247               unit->enabled_gl_target = gl_target;
248             }
249         }
250     }
251   else
252     {
253       /* Even though there may be no difference between the last flushed
254        * texture state and the current layers texture state it may be that the
255        * texture unit has been disabled for some time so we need to assert that
256        * it's enabled now.
257        */
258       if (!G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_TEXTURING)) &&
259           unit->enabled_gl_target == 0)
260         {
261           _cogl_set_active_texture_unit (unit_index);
262           GE (ctx, glEnable (unit->gl_target));
263           unit->enabled_gl_target = unit->gl_target;
264         }
265     }
266
267   if (layers_difference & COGL_PIPELINE_LAYER_STATE_COMBINE)
268     {
269       CoglPipelineLayer *authority =
270         _cogl_pipeline_layer_get_authority (layer,
271                                             COGL_PIPELINE_LAYER_STATE_COMBINE);
272       CoglPipelineLayerBigState *big_state = authority->big_state;
273       GLenum sources[3];
274
275       GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE));
276
277       /* Set the combiner functions... */
278       GE (ctx, glTexEnvi (GL_TEXTURE_ENV,
279                           GL_COMBINE_RGB,
280                           big_state->texture_combine_rgb_func));
281       GE (ctx, glTexEnvi (GL_TEXTURE_ENV,
282                           GL_COMBINE_ALPHA,
283                           big_state->texture_combine_alpha_func));
284
285       /*
286        * Setup the function arguments...
287        */
288
289       /* For the RGB components... */
290       n_rgb_func_args =
291         _cogl_get_n_args_for_combine_func (big_state->texture_combine_rgb_func);
292
293       translate_sources (pipeline,
294                          n_rgb_func_args,
295                          big_state->texture_combine_rgb_src,
296                          sources);
297
298       GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB,
299                           sources[0]));
300       GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB,
301                           big_state->texture_combine_rgb_op[0]));
302       if (n_rgb_func_args > 1)
303         {
304           GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB,
305                               sources[1]));
306           GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB,
307                               big_state->texture_combine_rgb_op[1]));
308         }
309       if (n_rgb_func_args > 2)
310         {
311           GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC2_RGB,
312                               sources[2]));
313           GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_RGB,
314                               big_state->texture_combine_rgb_op[2]));
315         }
316
317       /* For the Alpha component */
318       n_alpha_func_args =
319         _cogl_get_n_args_for_combine_func (big_state->texture_combine_alpha_func);
320
321       translate_sources (pipeline,
322                          n_alpha_func_args,
323                          big_state->texture_combine_alpha_src,
324                          sources);
325
326       GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA,
327                           sources[0]));
328       GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA,
329                           big_state->texture_combine_alpha_op[0]));
330       if (n_alpha_func_args > 1)
331         {
332           GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA,
333                               sources[1]));
334           GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA,
335                               big_state->texture_combine_alpha_op[1]));
336         }
337       if (n_alpha_func_args > 2)
338         {
339           GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_SRC2_ALPHA,
340                               sources[2]));
341           GE (ctx, glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_ALPHA,
342                               big_state->texture_combine_alpha_op[2]));
343         }
344     }
345
346   if (layers_difference & COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT)
347     {
348       CoglPipelineLayer *authority =
349         _cogl_pipeline_layer_get_authority
350         (layer, COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT);
351       CoglPipelineLayerBigState *big_state = authority->big_state;
352
353       GE (ctx, glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,
354                            big_state->texture_combine_constant));
355     }
356
357   return TRUE;
358 }
359
360 static gboolean
361 get_highest_unit_index_cb (CoglPipelineLayer *layer,
362                            void *user_data)
363 {
364   int unit_index = _cogl_pipeline_layer_get_unit_index (layer);
365   int *highest_index = user_data;
366
367   *highest_index = unit_index;
368
369   return TRUE;
370 }
371
372 static gboolean
373 _cogl_pipeline_fragend_fixed_end (CoglPipeline *pipeline,
374                                   unsigned long pipelines_difference)
375 {
376   int highest_unit_index = -1;
377   int i;
378
379   _COGL_GET_CONTEXT (ctx, FALSE);
380
381   _cogl_pipeline_foreach_layer_internal (pipeline,
382                                          get_highest_unit_index_cb,
383                                          &highest_unit_index);
384
385   /* Disable additional texture units that may have previously been in use.. */
386   for (i = highest_unit_index + 1; i < ctx->texture_units->len; i++)
387     _cogl_disable_texture_unit (i);
388
389   if (pipelines_difference & COGL_PIPELINE_STATE_FOG)
390     {
391       CoglPipeline *authority =
392         _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_FOG);
393       CoglPipelineFogState *fog_state = &authority->big_state->fog_state;
394
395       if (fog_state->enabled)
396         {
397           GLfloat fogColor[4];
398           GLenum gl_mode = GL_LINEAR;
399
400           fogColor[0] = cogl_color_get_red_float (&fog_state->color);
401           fogColor[1] = cogl_color_get_green_float (&fog_state->color);
402           fogColor[2] = cogl_color_get_blue_float (&fog_state->color);
403           fogColor[3] = cogl_color_get_alpha_float (&fog_state->color);
404
405           GE (ctx, glEnable (GL_FOG));
406
407           GE (ctx, glFogfv (GL_FOG_COLOR, fogColor));
408
409           if (ctx->driver == COGL_DRIVER_GLES1)
410             switch (fog_state->mode)
411               {
412               case COGL_FOG_MODE_LINEAR:
413                 gl_mode = GL_LINEAR;
414                 break;
415               case COGL_FOG_MODE_EXPONENTIAL:
416                 gl_mode = GL_EXP;
417                 break;
418               case COGL_FOG_MODE_EXPONENTIAL_SQUARED:
419                 gl_mode = GL_EXP2;
420                 break;
421               }
422           /* TODO: support other modes for GLES2 */
423
424           /* NB: GLES doesn't have glFogi */
425           GE (ctx, glFogf (GL_FOG_MODE, gl_mode));
426           GE (ctx, glHint (GL_FOG_HINT, GL_NICEST));
427
428           GE (ctx, glFogf (GL_FOG_DENSITY, fog_state->density));
429           GE (ctx, glFogf (GL_FOG_START, fog_state->z_near));
430           GE (ctx, glFogf (GL_FOG_END, fog_state->z_far));
431         }
432       else
433         GE (ctx, glDisable (GL_FOG));
434     }
435
436   return TRUE;
437 }
438
439 const CoglPipelineFragend _cogl_pipeline_fixed_fragend =
440 {
441   _cogl_pipeline_fragend_fixed_start,
442   _cogl_pipeline_fragend_fixed_add_layer,
443   NULL, /* passthrough */
444   _cogl_pipeline_fragend_fixed_end,
445   NULL, /* pipeline_change_notify */
446   NULL, /* pipeline_set_parent_notify */
447   NULL, /* layer_change_notify */
448 };
449
450 #endif /* COGL_PIPELINE_FRAGEND_FIXED */
451