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