"Initial commit to Gerrit"
[profile/ivi/cogl.git] / cogl / cogl-program.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 <http://www.gnu.org/licenses/>.
20  *
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28
29 #include "cogl-util.h"
30 #include "cogl-internal.h"
31 #include "cogl-context-private.h"
32 #include "cogl-handle.h"
33
34 #include "cogl-shader-private.h"
35 #include "cogl-program-private.h"
36
37 #include <string.h>
38
39 static void _cogl_program_free (CoglProgram *program);
40
41 COGL_HANDLE_DEFINE (Program, program);
42 COGL_OBJECT_DEFINE_DEPRECATED_REF_COUNTING (program);
43
44 /* A CoglProgram is effectively just a list of shaders that will be
45    used together and a set of values for the custom uniforms. No
46    actual GL program is created - instead this is the responsibility
47    of the GLSL material backend. The uniform values are collected in
48    an array and then flushed whenever the material backend requests
49    it. */
50
51 static void
52 _cogl_program_free (CoglProgram *program)
53 {
54   int i;
55
56   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
57
58   /* Unref all of the attached shaders */
59   g_slist_foreach (program->attached_shaders, (GFunc) cogl_handle_unref, NULL);
60   /* Destroy the list */
61   g_slist_free (program->attached_shaders);
62
63   for (i = 0; i < program->custom_uniforms->len; i++)
64     {
65       CoglProgramUniform *uniform =
66         &g_array_index (program->custom_uniforms, CoglProgramUniform, i);
67
68       g_free (uniform->name);
69
70       if (uniform->value.count > 1)
71         g_free (uniform->value.v.array);
72     }
73
74   g_array_free (program->custom_uniforms, TRUE);
75
76   g_slice_free (CoglProgram, program);
77 }
78
79 CoglHandle
80 cogl_create_program (void)
81 {
82   CoglProgram *program;
83
84   program = g_slice_new0 (CoglProgram);
85
86   program->custom_uniforms =
87     g_array_new (FALSE, FALSE, sizeof (CoglProgramUniform));
88   program->age = 0;
89
90   return _cogl_program_handle_new (program);
91 }
92
93 void
94 cogl_program_attach_shader (CoglHandle program_handle,
95                             CoglHandle shader_handle)
96 {
97   CoglProgram *program;
98   CoglShader *shader;
99
100   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
101
102   if (!cogl_is_program (program_handle) || !cogl_is_shader (shader_handle))
103     return;
104
105   program = program_handle;
106   shader = shader_handle;
107
108   /* Only one shader is allowed if the type is ARBfp */
109   if (shader->language == COGL_SHADER_LANGUAGE_ARBFP)
110     _COGL_RETURN_IF_FAIL (program->attached_shaders == NULL);
111   else if (shader->language == COGL_SHADER_LANGUAGE_GLSL)
112     _COGL_RETURN_IF_FAIL (_cogl_program_get_language (program) ==
113                       COGL_SHADER_LANGUAGE_GLSL);
114
115   program->attached_shaders
116     = g_slist_prepend (program->attached_shaders,
117                        cogl_handle_ref (shader_handle));
118
119   program->age++;
120 }
121
122 void
123 cogl_program_link (CoglHandle handle)
124 {
125   /* There's no point in linking the program here because it will have
126      to be relinked with a different fixed functionality shader
127      whenever the settings change */
128 }
129
130 void
131 cogl_program_use (CoglHandle handle)
132 {
133   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
134
135   _COGL_RETURN_IF_FAIL (handle == COGL_INVALID_HANDLE ||
136                     cogl_is_program (handle));
137
138   if (ctx->current_program == 0 && handle != 0)
139     ctx->legacy_state_set++;
140   else if (handle == 0 && ctx->current_program != 0)
141     ctx->legacy_state_set--;
142
143   if (handle != COGL_INVALID_HANDLE)
144     cogl_handle_ref (handle);
145   if (ctx->current_program != COGL_INVALID_HANDLE)
146     cogl_handle_unref (ctx->current_program);
147   ctx->current_program = handle;
148 }
149
150 int
151 cogl_program_get_uniform_location (CoglHandle handle,
152                                    const char *uniform_name)
153 {
154   int i;
155   CoglProgram *program;
156   CoglProgramUniform *uniform;
157
158   if (!cogl_is_program (handle))
159     return -1;
160
161   program = handle;
162
163   /* We can't just ask the GL program object for the uniform location
164      directly because it will change every time the program is linked
165      with a different shader. Instead we make our own mapping of
166      uniform numbers and cache the names */
167   for (i = 0; i < program->custom_uniforms->len; i++)
168     {
169       uniform = &g_array_index (program->custom_uniforms,
170                                 CoglProgramUniform, i);
171
172       if (!strcmp (uniform->name, uniform_name))
173         return i;
174     }
175
176   /* Create a new uniform with the given name */
177   g_array_set_size (program->custom_uniforms,
178                     program->custom_uniforms->len + 1);
179   uniform = &g_array_index (program->custom_uniforms,
180                             CoglProgramUniform,
181                             program->custom_uniforms->len - 1);
182
183   uniform->name = g_strdup (uniform_name);
184   memset (&uniform->value, 0, sizeof (CoglBoxedValue));
185   uniform->dirty = TRUE;
186   uniform->location_valid = FALSE;
187
188   return program->custom_uniforms->len - 1;
189 }
190
191 static CoglProgramUniform *
192 cogl_program_modify_uniform (CoglProgram *program,
193                              int uniform_no)
194 {
195   CoglProgramUniform *uniform;
196
197   _COGL_RETURN_VAL_IF_FAIL (cogl_is_program (program), NULL);
198   _COGL_RETURN_VAL_IF_FAIL (uniform_no >= 0 &&
199                             uniform_no < program->custom_uniforms->len,
200                             NULL);
201
202   uniform = &g_array_index (program->custom_uniforms,
203                             CoglProgramUniform, uniform_no);
204   uniform->dirty = TRUE;
205
206   return uniform;
207 }
208
209 void
210 cogl_program_uniform_1f (int uniform_no,
211                          float  value)
212 {
213   CoglProgramUniform *uniform;
214
215   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
216
217   uniform = cogl_program_modify_uniform (ctx->current_program, uniform_no);
218   _cogl_boxed_value_set_1f (&uniform->value, value);
219 }
220
221 void
222 cogl_program_set_uniform_1f (CoglHandle handle,
223                              int uniform_location,
224                              float value)
225 {
226   CoglProgramUniform *uniform;
227
228   uniform = cogl_program_modify_uniform (handle, uniform_location);
229   _cogl_boxed_value_set_1f (&uniform->value, value);
230 }
231
232 void
233 cogl_program_uniform_1i (int uniform_no,
234                          int value)
235 {
236   CoglProgramUniform *uniform;
237
238   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
239
240   uniform = cogl_program_modify_uniform (ctx->current_program, uniform_no);
241   _cogl_boxed_value_set_1i (&uniform->value, value);
242 }
243
244 void
245 cogl_program_set_uniform_1i (CoglHandle handle,
246                              int uniform_location,
247                              int value)
248 {
249   CoglProgramUniform *uniform;
250
251   uniform = cogl_program_modify_uniform (handle, uniform_location);
252   _cogl_boxed_value_set_1i (&uniform->value, value);
253 }
254
255 void
256 cogl_program_uniform_float (int uniform_no,
257                             int size,
258                             int count,
259                             const float *value)
260 {
261   CoglProgramUniform *uniform;
262
263   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
264
265   uniform = cogl_program_modify_uniform (ctx->current_program, uniform_no);
266   _cogl_boxed_value_set_float (&uniform->value, size, count, value);
267 }
268
269 void
270 cogl_program_set_uniform_float (CoglHandle handle,
271                                 int uniform_location,
272                                 int n_components,
273                                 int count,
274                                 const float *value)
275 {
276   CoglProgramUniform *uniform;
277
278   uniform = cogl_program_modify_uniform (handle, uniform_location);
279   _cogl_boxed_value_set_float (&uniform->value, n_components, count, value);
280 }
281
282 void
283 cogl_program_uniform_int (int uniform_no,
284                           int size,
285                           int count,
286                           const int *value)
287 {
288   CoglProgramUniform *uniform;
289
290   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
291
292   uniform = cogl_program_modify_uniform (ctx->current_program, uniform_no);
293   _cogl_boxed_value_set_int (&uniform->value, size, count, value);
294 }
295
296 void
297 cogl_program_set_uniform_int (CoglHandle handle,
298                               int uniform_location,
299                               int n_components,
300                               int count,
301                               const int *value)
302 {
303   CoglProgramUniform *uniform;
304
305   uniform = cogl_program_modify_uniform (handle, uniform_location);
306   _cogl_boxed_value_set_int (&uniform->value, n_components, count, value);
307 }
308
309 void
310 cogl_program_set_uniform_matrix (CoglHandle handle,
311                                  int uniform_location,
312                                  int dimensions,
313                                  int count,
314                                  gboolean transpose,
315                                  const float *value)
316 {
317   CoglProgramUniform *uniform;
318
319   uniform = cogl_program_modify_uniform (handle, uniform_location);
320   _cogl_boxed_value_set_matrix (&uniform->value,
321                                 dimensions,
322                                 count,
323                                 transpose,
324                                 value);
325 }
326
327 void
328 cogl_program_uniform_matrix (int uniform_no,
329                              int size,
330                              int count,
331                              gboolean transpose,
332                              const float *value)
333 {
334   CoglProgramUniform *uniform;
335
336   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
337
338   uniform = cogl_program_modify_uniform (ctx->current_program, uniform_no);
339   _cogl_boxed_value_set_matrix (&uniform->value, size, count, transpose, value);
340 }
341
342 /* ARBfp local parameters can be referenced like:
343  *
344  * "program.local[5]"
345  *                ^14char offset (after whitespace is stripped)
346  */
347 static int
348 get_local_param_index (const char *uniform_name)
349 {
350   char *input = g_strdup (uniform_name);
351   int i;
352   char *p = input;
353   char *endptr;
354   int _index;
355
356   for (i = 0; input[i] != '\0'; i++)
357     if (input[i] != '_' && input[i] != '\t')
358       *p++ = input[i];
359   input[i] = '\0';
360
361   _COGL_RETURN_VAL_IF_FAIL (strncmp ("program.local[", input, 14) == 0, -1);
362
363   _index = g_ascii_strtoull (input + 14, &endptr, 10);
364   _COGL_RETURN_VAL_IF_FAIL (endptr != input + 14, -1);
365   _COGL_RETURN_VAL_IF_FAIL (*endptr == ']', -1);
366
367   _COGL_RETURN_VAL_IF_FAIL (_index >= 0, -1);
368
369   g_free (input);
370
371   return _index;
372 }
373
374 #ifdef HAVE_COGL_GL
375
376 static void
377 _cogl_program_flush_uniform_arbfp (GLint location,
378                                    CoglBoxedValue *value)
379 {
380   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
381
382   if (value->type != COGL_BOXED_NONE)
383     {
384       _COGL_RETURN_IF_FAIL (value->type == COGL_BOXED_FLOAT);
385       _COGL_RETURN_IF_FAIL (value->size == 4);
386       _COGL_RETURN_IF_FAIL (value->count == 1);
387
388       GE( ctx, glProgramLocalParameter4fv (GL_FRAGMENT_PROGRAM_ARB, location,
389                                            value->v.float_value) );
390     }
391 }
392
393 #endif /* HAVE_COGL_GL */
394
395 void
396 _cogl_program_flush_uniforms (CoglProgram *program,
397                               GLuint gl_program,
398                               gboolean gl_program_changed)
399 {
400   CoglProgramUniform *uniform;
401   int i;
402
403   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
404
405   _COGL_RETURN_IF_FAIL (ctx->driver != COGL_DRIVER_GLES1);
406
407   for (i = 0; i < program->custom_uniforms->len; i++)
408     {
409       uniform = &g_array_index (program->custom_uniforms,
410                                 CoglProgramUniform, i);
411
412       if (gl_program_changed || uniform->dirty)
413         {
414           if (gl_program_changed || !uniform->location_valid)
415             {
416               if (_cogl_program_get_language (program) ==
417                   COGL_SHADER_LANGUAGE_GLSL)
418                 uniform->location =
419                   ctx->glGetUniformLocation (gl_program, uniform->name);
420               else
421                 uniform->location =
422                   get_local_param_index (uniform->name);
423
424               uniform->location_valid = TRUE;
425             }
426
427           /* If the uniform isn't really in the program then there's
428              no need to actually set it */
429           if (uniform->location != -1)
430             {
431               switch (_cogl_program_get_language (program))
432                 {
433                 case COGL_SHADER_LANGUAGE_GLSL:
434                   _cogl_boxed_value_set_uniform (ctx,
435                                                  uniform->location,
436                                                  &uniform->value);
437                   break;
438
439                 case COGL_SHADER_LANGUAGE_ARBFP:
440 #ifdef HAVE_COGL_GL
441                   _cogl_program_flush_uniform_arbfp (uniform->location,
442                                                      &uniform->value);
443 #endif
444                   break;
445                 }
446             }
447
448           uniform->dirty = FALSE;
449         }
450     }
451 }
452
453 CoglShaderLanguage
454 _cogl_program_get_language (CoglHandle handle)
455 {
456   CoglProgram *program = handle;
457
458   /* Use the language of the first shader */
459
460   if (program->attached_shaders)
461     {
462       CoglShader *shader = program->attached_shaders->data;
463       return shader->language;
464     }
465   else
466     return COGL_SHADER_LANGUAGE_GLSL;
467 }
468
469 static gboolean
470 _cogl_program_has_shader_type (CoglProgram *program,
471                                CoglShaderType type)
472 {
473   GSList *l;
474
475   for (l = program->attached_shaders; l; l = l->next)
476     {
477       CoglShader *shader = l->data;
478
479       if (shader->type == type)
480         return TRUE;
481     }
482
483   return FALSE;
484 }
485
486 gboolean
487 _cogl_program_has_fragment_shader (CoglHandle handle)
488 {
489   return _cogl_program_has_shader_type (handle, COGL_SHADER_TYPE_FRAGMENT);
490 }
491
492 gboolean
493 _cogl_program_has_vertex_shader (CoglHandle handle)
494 {
495   return _cogl_program_has_shader_type (handle, COGL_SHADER_TYPE_VERTEX);
496 }