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