1 /*
2  * Cogl
3  *
4  * A Low Level GPU Graphics and Utilities API
5  *
6  * Copyright (C) 2007,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 
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 
35 #include "cogl-shader-private.h"
36 #include "cogl-util-gl-private.h"
37 #include "cogl-context-private.h"
38 #include "cogl-object-private.h"
39 #include "cogl-glsl-shader-private.h"
40 #include "cogl-glsl-shader-boilerplate.h"
41 
42 #include <glib.h>
43 
44 #include <string.h>
45 
46 static void _cogl_shader_free (CoglShader *shader);
47 
48 COGL_HANDLE_DEFINE (Shader, shader);
49 COGL_OBJECT_DEFINE_DEPRECATED_REF_COUNTING (shader);
50 
51 #ifndef GL_FRAGMENT_SHADER
52 #define GL_FRAGMENT_SHADER 0x8B30
53 #endif
54 #ifndef GL_VERTEX_SHADER
55 #define GL_VERTEX_SHADER 0x8B31
56 #endif
57 
58 static void
_cogl_shader_free(CoglShader * shader)59 _cogl_shader_free (CoglShader *shader)
60 {
61   /* Frees shader resources but its handle is not
62      released! Do that separately before this! */
63   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
64 
65 #ifdef HAVE_COGL_GL
66   if (shader->language == COGL_SHADER_LANGUAGE_ARBFP)
67     {
68       if (shader->gl_handle)
69         GE (ctx, glDeletePrograms (1, &shader->gl_handle));
70     }
71   else
72 #endif
73     if (shader->gl_handle)
74       GE (ctx, glDeleteShader (shader->gl_handle));
75 
76   g_slice_free (CoglShader, shader);
77 }
78 
79 CoglHandle
cogl_create_shader(CoglShaderType type)80 cogl_create_shader (CoglShaderType type)
81 {
82   CoglShader *shader;
83 
84   _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
85 
86   switch (type)
87     {
88     case COGL_SHADER_TYPE_VERTEX:
89     case COGL_SHADER_TYPE_FRAGMENT:
90       break;
91     default:
92       g_warning ("Unexpected shader type (0x%08lX) given to "
93                  "cogl_create_shader", (unsigned long) type);
94       return COGL_INVALID_HANDLE;
95     }
96 
97   shader = g_slice_new (CoglShader);
98   shader->language = COGL_SHADER_LANGUAGE_GLSL;
99   shader->gl_handle = 0;
100   shader->compilation_pipeline = NULL;
101   shader->type = type;
102 
103   return _cogl_shader_handle_new (shader);
104 }
105 
106 static void
delete_shader(CoglShader * shader)107 delete_shader (CoglShader *shader)
108 {
109   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
110 
111 #ifdef HAVE_COGL_GL
112   if (shader->language == COGL_SHADER_LANGUAGE_ARBFP)
113     {
114       if (shader->gl_handle)
115         GE (ctx, glDeletePrograms (1, &shader->gl_handle));
116     }
117   else
118 #endif
119     {
120       if (shader->gl_handle)
121         GE (ctx, glDeleteShader (shader->gl_handle));
122     }
123 
124   shader->gl_handle = 0;
125 
126   if (shader->compilation_pipeline)
127     {
128       cogl_object_unref (shader->compilation_pipeline);
129       shader->compilation_pipeline = NULL;
130     }
131 }
132 
133 void
cogl_shader_source(CoglHandle handle,const char * source)134 cogl_shader_source (CoglHandle   handle,
135                     const char  *source)
136 {
137   CoglShader *shader;
138   CoglShaderLanguage language;
139 
140   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
141 
142   if (!cogl_is_shader (handle))
143     return;
144 
145   shader = handle;
146 
147 #ifdef HAVE_COGL_GL
148   if (strncmp (source, "!!ARBfp1.0", 10) == 0)
149     language = COGL_SHADER_LANGUAGE_ARBFP;
150   else
151 #endif
152     language = COGL_SHADER_LANGUAGE_GLSL;
153 
154   /* Delete the old object if the language is changing... */
155   if (G_UNLIKELY (language != shader->language) &&
156       shader->gl_handle)
157     delete_shader (shader);
158 
159   shader->source = g_strdup (source);
160 
161   shader->language = language;
162 }
163 
164 void
cogl_shader_compile(CoglHandle handle)165 cogl_shader_compile (CoglHandle handle)
166 {
167   CoglShader *shader;
168 
169   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
170 
171   if (!cogl_is_shader (handle))
172     return;
173 
174 #ifdef HAVE_COGL_GL
175   shader = handle;
176   if (shader->language == COGL_SHADER_LANGUAGE_ARBFP)
177     _cogl_shader_compile_real (handle, NULL);
178 #endif
179 
180   /* XXX: For GLSL we don't actually compile anything until the shader
181    * gets used so we have an opportunity to add some boilerplate to
182    * the shader.
183    *
184    * At the end of the day this is obviously a badly designed API
185    * given that we are having to lie to the user. It was a mistake to
186    * so thinly wrap the OpenGL shader API and the current plan is to
187    * replace it with a pipeline snippets API. */
188 }
189 
190 void
_cogl_shader_compile_real(CoglHandle handle,CoglPipeline * pipeline)191 _cogl_shader_compile_real (CoglHandle handle,
192                            CoglPipeline *pipeline)
193 {
194   CoglShader *shader = handle;
195 
196   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
197 
198 #ifdef HAVE_COGL_GL
199   if (shader->language == COGL_SHADER_LANGUAGE_ARBFP)
200     {
201 #ifdef COGL_GL_DEBUG
202       GLenum gl_error;
203 #endif
204 
205       if (shader->gl_handle)
206         return;
207 
208       GE (ctx, glGenPrograms (1, &shader->gl_handle));
209 
210       GE (ctx, glBindProgram (GL_FRAGMENT_PROGRAM_ARB, shader->gl_handle));
211 
212       if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_SHOW_SOURCE)))
213         g_message ("user ARBfp program:\n%s", shader->source);
214 
215 #ifdef COGL_GL_DEBUG
216       while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR)
217         ;
218 #endif
219       ctx->glProgramString (GL_FRAGMENT_PROGRAM_ARB,
220                             GL_PROGRAM_FORMAT_ASCII_ARB,
221                             strlen (shader->source),
222                             shader->source);
223 #ifdef COGL_GL_DEBUG
224       gl_error = ctx->glGetError ();
225       if (gl_error != GL_NO_ERROR)
226         {
227           g_warning ("%s: GL error (%d): Failed to compile ARBfp:\n%s\n%s",
228                      G_STRLOC,
229                      gl_error,
230                      shader->source,
231                      ctx->glGetString (GL_PROGRAM_ERROR_STRING_ARB));
232         }
233 #endif
234     }
235   else
236 #endif
237     {
238       GLenum gl_type;
239       GLint status;
240 
241       if (shader->gl_handle)
242         {
243           CoglPipeline *prev = shader->compilation_pipeline;
244 
245           /* XXX: currently the only things that will affect the
246            * boilerplate for user shaders, apart from driver features,
247            * are the pipeline layer-indices and texture-unit-indices
248            */
249           if (pipeline == prev ||
250               _cogl_pipeline_layer_and_unit_numbers_equal (prev, pipeline))
251             return;
252         }
253 
254       if (shader->gl_handle)
255         delete_shader (shader);
256 
257       switch (shader->type)
258         {
259         case COGL_SHADER_TYPE_VERTEX:
260           gl_type = GL_VERTEX_SHADER;
261           break;
262         case COGL_SHADER_TYPE_FRAGMENT:
263           gl_type = GL_FRAGMENT_SHADER;
264           break;
265         default:
266           g_assert_not_reached ();
267           break;
268         }
269 
270       shader->gl_handle = ctx->glCreateShader (gl_type);
271 
272       _cogl_glsl_shader_set_source_with_boilerplate (ctx,
273                                                      shader->gl_handle,
274                                                      gl_type,
275                                                      pipeline,
276                                                      1,
277                                                      (const char **)
278                                                       &shader->source,
279                                                      NULL);
280 
281       GE (ctx, glCompileShader (shader->gl_handle));
282 
283       shader->compilation_pipeline = cogl_object_ref (pipeline);
284 
285       GE (ctx, glGetShaderiv (shader->gl_handle, GL_COMPILE_STATUS, &status));
286       if (!status)
287         {
288           char buffer[512];
289           int len = 0;
290 
291           ctx->glGetShaderInfoLog (shader->gl_handle, 511, &len, buffer);
292           buffer[len] = '\0';
293 
294           g_warning ("Failed to compile GLSL program:\n"
295                      "src:\n%s\n"
296                      "error:\n%s\n",
297                      shader->source,
298                      buffer);
299         }
300     }
301 }
302 
303 char *
cogl_shader_get_info_log(CoglHandle handle)304 cogl_shader_get_info_log (CoglHandle handle)
305 {
306   if (!cogl_is_shader (handle))
307     return NULL;
308 
309   /* XXX: This API doesn't really do anything!
310    *
311    * This API is purely for compatibility
312    *
313    * The reason we don't do anything is because a shader needs to
314    * be associated with a CoglPipeline for Cogl to be able to
315    * compile and link anything.
316    *
317    * The way this API was originally designed as a very thin wrapper
318    * over the GL api was a mistake and it's now very difficult to
319    * make the API work in a meaningful way given how the rest of Cogl
320    * has evolved.
321    *
322    * The CoglShader API is mostly deprecated by CoglSnippets and so
323    * these days we do the bare minimum to support the existing users
324    * of it until they are able to migrate to the snippets api.
325    */
326 
327   return g_strdup ("");
328 }
329 
330 CoglShaderType
cogl_shader_get_type(CoglHandle handle)331 cogl_shader_get_type (CoglHandle  handle)
332 {
333   CoglShader *shader;
334 
335   _COGL_GET_CONTEXT (ctx, COGL_SHADER_TYPE_VERTEX);
336 
337   if (!cogl_is_shader (handle))
338     {
339       g_warning ("Non shader handle type passed to cogl_shader_get_type");
340       return COGL_SHADER_TYPE_VERTEX;
341     }
342 
343   shader = handle;
344   return shader->type;
345 }
346 
347 CoglBool
cogl_shader_is_compiled(CoglHandle handle)348 cogl_shader_is_compiled (CoglHandle handle)
349 {
350 #if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES2)
351   if (!cogl_is_shader (handle))
352     return FALSE;
353 
354   /* XXX: This API doesn't really do anything!
355    *
356    * This API is purely for compatibility and blatantly lies to the
357    * user about whether their shader has been compiled.
358    *
359    * I suppose we could say we're stretching the definition of
360    * "compile" and are deferring any related errors to be "linker"
361    * errors.
362    *
363    * The reason we don't do anything is because a shader needs to
364    * be associated with a CoglPipeline for Cogl to be able to
365    * compile and link anything.
366    *
367    * The CoglShader API is mostly deprecated by CoglSnippets and so
368    * these days we do the bare minimum to support the existing users
369    * of it until they are able to migrate to the snippets api.
370    */
371 
372   return TRUE;
373 
374 #else
375   return FALSE;
376 #endif
377 }
378