Line data Source code
1 : /* ______ ___ ___
2 : * /\ _ \ /\_ \ /\_ \
3 : * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
4 : * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
5 : * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
6 : * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7 : * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8 : * /\____/
9 : * \_/__/
10 : *
11 : * OpenGL shader support.
12 : *
13 : * See LICENSE.txt for copyright information.
14 : */
15 :
16 : #include <stdio.h>
17 :
18 : #include "allegro5/allegro.h"
19 : #include "allegro5/allegro_opengl.h"
20 : #include "allegro5/internal/aintern_bitmap.h"
21 : #include "allegro5/internal/aintern_display.h"
22 : #include "allegro5/internal/aintern_opengl.h"
23 : #include "allegro5/internal/aintern_shader.h"
24 :
25 : #ifdef ALLEGRO_MSVC
26 : #define snprintf _snprintf
27 : #endif
28 :
29 : #ifdef ALLEGRO_CFG_SHADER_GLSL
30 :
31 : ALLEGRO_DEBUG_CHANNEL("shader")
32 :
33 : static _AL_VECTOR shaders;
34 : static ALLEGRO_MUTEX *shaders_mutex;
35 :
36 : typedef struct ALLEGRO_SHADER_GLSL_S ALLEGRO_SHADER_GLSL_S;
37 :
38 : struct ALLEGRO_SHADER_GLSL_S
39 : {
40 : ALLEGRO_SHADER shader;
41 : GLuint vertex_shader;
42 : GLuint pixel_shader;
43 : GLuint program_object;
44 : ALLEGRO_OGL_VARLOCS varlocs;
45 : };
46 :
47 :
48 : /* forward declarations */
49 : static struct ALLEGRO_SHADER_INTERFACE shader_glsl_vt;
50 : static void lookup_varlocs(ALLEGRO_OGL_VARLOCS *varlocs, GLuint program);
51 :
52 :
53 0 : static bool check_gl_error(const char* name)
54 : {
55 0 : GLenum err = glGetError();
56 0 : if (err != 0) {
57 0 : ALLEGRO_WARN("%s (%s)\n", name, _al_gl_error_string(err));
58 : return false;
59 : }
60 : return true;
61 : }
62 :
63 :
64 0 : ALLEGRO_SHADER *_al_create_shader_glsl(ALLEGRO_SHADER_PLATFORM platform)
65 : {
66 0 : ALLEGRO_SHADER_GLSL_S *shader = al_calloc(1, sizeof(ALLEGRO_SHADER_GLSL_S));
67 :
68 0 : if (!shader)
69 : return NULL;
70 :
71 0 : shader->shader.platform = platform;
72 0 : shader->shader.vt = &shader_glsl_vt;
73 0 : _al_vector_init(&shader->shader.bitmaps, sizeof(ALLEGRO_BITMAP *));
74 :
75 0 : al_lock_mutex(shaders_mutex);
76 : {
77 0 : ALLEGRO_SHADER **back = (ALLEGRO_SHADER **)_al_vector_alloc_back(&shaders);
78 0 : *back = (ALLEGRO_SHADER *)shader;
79 : }
80 0 : al_unlock_mutex(shaders_mutex);
81 :
82 0 : return (ALLEGRO_SHADER *)shader;
83 : }
84 :
85 0 : static bool glsl_attach_shader_source(ALLEGRO_SHADER *shader,
86 : ALLEGRO_SHADER_TYPE type, const char *source)
87 : {
88 : GLint status;
89 : GLchar error_buf[4096];
90 0 : ALLEGRO_SHADER_GLSL_S *gl_shader = (ALLEGRO_SHADER_GLSL_S *)shader;
91 0 : ALLEGRO_DISPLAY *display = al_get_current_display();
92 : ASSERT(display);
93 : ASSERT(display->flags & ALLEGRO_OPENGL);
94 :
95 0 : if (source == NULL) {
96 0 : if (type == ALLEGRO_VERTEX_SHADER) {
97 0 : if (gl_shader->vertex_shader) {
98 0 : glDetachShader(gl_shader->program_object, gl_shader->vertex_shader);
99 0 : glDeleteShader(gl_shader->vertex_shader);
100 0 : gl_shader->vertex_shader = 0;
101 : }
102 : }
103 : else {
104 0 : if (gl_shader->pixel_shader) {
105 0 : glDetachShader(gl_shader->program_object, gl_shader->pixel_shader);
106 0 : glDeleteShader(gl_shader->pixel_shader);
107 0 : gl_shader->pixel_shader = 0;
108 : }
109 : }
110 : return true;
111 : }
112 : else {
113 : GLuint *handle;
114 : GLenum gl_type;
115 0 : if (type == ALLEGRO_VERTEX_SHADER) {
116 0 : handle = &(gl_shader->vertex_shader);
117 0 : gl_type = GL_VERTEX_SHADER;
118 : }
119 : else {
120 0 : handle = &(gl_shader->pixel_shader);
121 0 : gl_type = GL_FRAGMENT_SHADER;
122 : }
123 0 : *handle = glCreateShader(gl_type);
124 0 : if ((*handle) == 0) {
125 : return false;
126 : }
127 0 : glShaderSource(*handle, 1, &source, NULL);
128 0 : glCompileShader(*handle);
129 0 : glGetShaderiv(*handle, GL_COMPILE_STATUS, &status);
130 0 : if (status == 0) {
131 0 : glGetShaderInfoLog(*handle, sizeof(error_buf), NULL, error_buf);
132 0 : if (shader->log) {
133 0 : al_ustr_truncate(shader->log, 0);
134 0 : al_ustr_append_cstr(shader->log, error_buf);
135 : }
136 : else {
137 0 : shader->log = al_ustr_new(error_buf);
138 : }
139 0 : ALLEGRO_ERROR("Compile error: %s\n", error_buf);
140 0 : glDeleteShader(*handle);
141 0 : return false;
142 : }
143 : }
144 :
145 : return true;
146 : }
147 :
148 0 : static bool glsl_build_shader(ALLEGRO_SHADER *shader)
149 : {
150 : GLint status;
151 0 : ALLEGRO_SHADER_GLSL_S *gl_shader = (ALLEGRO_SHADER_GLSL_S *)shader;
152 : GLchar error_buf[4096];
153 :
154 0 : if (gl_shader->vertex_shader == 0 && gl_shader->pixel_shader == 0)
155 : return false;
156 :
157 0 : if (gl_shader->program_object != 0) {
158 0 : glDeleteProgram(gl_shader->program_object);
159 : }
160 :
161 0 : gl_shader->program_object = glCreateProgram();
162 0 : if (gl_shader->program_object == 0)
163 : return false;
164 :
165 0 : if (gl_shader->vertex_shader)
166 0 : glAttachShader(gl_shader->program_object, gl_shader->vertex_shader);
167 0 : if (gl_shader->pixel_shader)
168 0 : glAttachShader(gl_shader->program_object, gl_shader->pixel_shader);
169 :
170 0 : glLinkProgram(gl_shader->program_object);
171 :
172 0 : glGetProgramiv(gl_shader->program_object, GL_LINK_STATUS, &status);
173 :
174 0 : if (status == 0) {
175 0 : glGetProgramInfoLog(gl_shader->program_object, sizeof(error_buf), NULL,
176 : error_buf);
177 0 : if (shader->log) {
178 0 : al_ustr_truncate(shader->log, 0);
179 0 : al_ustr_append_cstr(shader->log, error_buf);
180 : }
181 : else {
182 0 : shader->log = al_ustr_new(error_buf);
183 : }
184 0 : ALLEGRO_ERROR("Link error: %s\n", error_buf);
185 0 : glDeleteProgram(gl_shader->program_object);
186 0 : return false;
187 : }
188 :
189 : /* Look up variable locations. */
190 0 : lookup_varlocs(&gl_shader->varlocs, gl_shader->program_object);
191 :
192 0 : return true;
193 : }
194 :
195 0 : static bool glsl_use_shader(ALLEGRO_SHADER *shader, ALLEGRO_DISPLAY *display,
196 : bool set_projview_matrix_from_display)
197 : {
198 : ALLEGRO_SHADER_GLSL_S *gl_shader;
199 : GLuint program_object;
200 : GLenum err;
201 :
202 0 : if (!(display->flags & ALLEGRO_OPENGL)) {
203 : return false;
204 : }
205 :
206 0 : gl_shader = (ALLEGRO_SHADER_GLSL_S *)shader;
207 0 : program_object = gl_shader->program_object;
208 :
209 0 : glGetError(); /* clear error */
210 0 : glUseProgram(program_object);
211 0 : err = glGetError();
212 0 : if (err != GL_NO_ERROR) {
213 0 : ALLEGRO_WARN("glUseProgram(%u) failed: %s\n", program_object,
214 : _al_gl_error_string(err));
215 0 : display->ogl_extras->program_object = 0;
216 0 : return false;
217 : }
218 :
219 0 : display->ogl_extras->program_object = program_object;
220 :
221 : /* Copy variable locations. */
222 0 : display->ogl_extras->varlocs = gl_shader->varlocs;
223 :
224 : /* Optionally set projview matrix. We skip this when it is known that the
225 : * matrices in the display are out of date and are about to be clobbered
226 : * itself.
227 : */
228 0 : if (set_projview_matrix_from_display) {
229 0 : _al_glsl_set_projview_matrix(
230 0 : display->ogl_extras->varlocs.projview_matrix_loc, &display->projview_transform);
231 : }
232 :
233 : return true;
234 : }
235 :
236 0 : static void glsl_unuse_shader(ALLEGRO_SHADER *shader, ALLEGRO_DISPLAY *display)
237 : {
238 : (void)shader;
239 : (void)display;
240 0 : glUseProgram(0);
241 0 : }
242 :
243 0 : static void glsl_destroy_shader(ALLEGRO_SHADER *shader)
244 : {
245 0 : ALLEGRO_SHADER_GLSL_S *gl_shader = (ALLEGRO_SHADER_GLSL_S *)shader;
246 :
247 0 : al_lock_mutex(shaders_mutex);
248 0 : _al_vector_find_and_delete(&shaders, &shader);
249 0 : al_unlock_mutex(shaders_mutex);
250 :
251 0 : glDeleteShader(gl_shader->vertex_shader);
252 0 : glDeleteShader(gl_shader->pixel_shader);
253 0 : glDeleteProgram(gl_shader->program_object);
254 0 : al_free(shader);
255 0 : }
256 :
257 0 : static bool glsl_set_shader_sampler(ALLEGRO_SHADER *shader,
258 : const char *name, ALLEGRO_BITMAP *bitmap, int unit)
259 : {
260 0 : ALLEGRO_SHADER_GLSL_S *gl_shader = (ALLEGRO_SHADER_GLSL_S *)shader;
261 : GLint handle;
262 : GLuint texture;
263 :
264 0 : if (bitmap && al_get_bitmap_flags(bitmap) & ALLEGRO_MEMORY_BITMAP) {
265 0 : ALLEGRO_WARN("Cannot use memory bitmap for sampler\n");
266 : return false;
267 : }
268 :
269 0 : handle = glGetUniformLocation(gl_shader->program_object, name);
270 :
271 0 : if (handle < 0) {
272 0 : ALLEGRO_WARN("No uniform variable '%s' in shader program\n", name);
273 : return false;
274 : }
275 :
276 0 : glActiveTexture(GL_TEXTURE0 + unit);
277 :
278 0 : texture = bitmap ? al_get_opengl_texture(bitmap) : 0;
279 0 : glBindTexture(GL_TEXTURE_2D, texture);
280 :
281 0 : glUniform1i(handle, unit);
282 :
283 0 : return check_gl_error(name);
284 : }
285 :
286 0 : static bool glsl_set_shader_matrix(ALLEGRO_SHADER *shader,
287 : const char *name, const ALLEGRO_TRANSFORM *matrix)
288 : {
289 0 : ALLEGRO_SHADER_GLSL_S *gl_shader = (ALLEGRO_SHADER_GLSL_S *)shader;
290 : GLint handle;
291 :
292 0 : handle = glGetUniformLocation(gl_shader->program_object, name);
293 :
294 0 : if (handle < 0) {
295 0 : ALLEGRO_WARN("No uniform variable '%s' in shader program\n", name);
296 : return false;
297 : }
298 :
299 0 : glUniformMatrix4fv(handle, 1, false, (const float *)matrix->m);
300 :
301 0 : return check_gl_error(name);
302 : }
303 :
304 0 : static bool glsl_set_shader_int(ALLEGRO_SHADER *shader,
305 : const char *name, int i)
306 : {
307 0 : ALLEGRO_SHADER_GLSL_S *gl_shader = (ALLEGRO_SHADER_GLSL_S *)shader;
308 : GLint handle;
309 :
310 0 : handle = glGetUniformLocation(gl_shader->program_object, name);
311 :
312 0 : if (handle < 0) {
313 0 : ALLEGRO_WARN("No uniform variable '%s' in shader program\n", name);
314 : return false;
315 : }
316 :
317 0 : glUniform1i(handle, i);
318 :
319 0 : return check_gl_error(name);
320 : }
321 :
322 0 : static bool glsl_set_shader_float(ALLEGRO_SHADER *shader,
323 : const char *name, float f)
324 : {
325 0 : ALLEGRO_SHADER_GLSL_S *gl_shader = (ALLEGRO_SHADER_GLSL_S *)shader;
326 : GLint handle;
327 :
328 0 : handle = glGetUniformLocation(gl_shader->program_object, name);
329 :
330 0 : if (handle < 0) {
331 0 : ALLEGRO_WARN("No uniform variable '%s' in shader program\n", name);
332 : return false;
333 : }
334 :
335 0 : glUniform1f(handle, f);
336 :
337 0 : return check_gl_error(name);
338 : }
339 :
340 0 : static bool glsl_set_shader_int_vector(ALLEGRO_SHADER *shader,
341 : const char *name, int num_components, const int *i, int num_elems)
342 : {
343 0 : ALLEGRO_SHADER_GLSL_S *gl_shader = (ALLEGRO_SHADER_GLSL_S *)shader;
344 : GLint handle;
345 :
346 0 : handle = glGetUniformLocation(gl_shader->program_object, name);
347 :
348 0 : if (handle < 0) {
349 0 : ALLEGRO_WARN("No uniform variable '%s' in shader program\n", name);
350 : return false;
351 : }
352 :
353 0 : switch (num_components) {
354 0 : case 1:
355 0 : glUniform1iv(handle, num_elems, i);
356 0 : break;
357 0 : case 2:
358 0 : glUniform2iv(handle, num_elems, i);
359 0 : break;
360 0 : case 3:
361 0 : glUniform3iv(handle, num_elems, i);
362 0 : break;
363 0 : case 4:
364 0 : glUniform4iv(handle, num_elems, i);
365 0 : break;
366 : default:
367 : ASSERT(false);
368 : break;
369 : }
370 :
371 0 : return check_gl_error(name);
372 : }
373 :
374 0 : static bool glsl_set_shader_float_vector(ALLEGRO_SHADER *shader,
375 : const char *name, int num_components, const float *f, int num_elems)
376 : {
377 0 : ALLEGRO_SHADER_GLSL_S *gl_shader = (ALLEGRO_SHADER_GLSL_S *)shader;
378 : GLint handle;
379 :
380 0 : handle = glGetUniformLocation(gl_shader->program_object, name);
381 :
382 0 : if (handle < 0) {
383 0 : ALLEGRO_WARN("No uniform variable '%s' in shader program\n", name);
384 : return false;
385 : }
386 :
387 0 : switch (num_components) {
388 0 : case 1:
389 0 : glUniform1fv(handle, num_elems, f);
390 0 : break;
391 0 : case 2:
392 0 : glUniform2fv(handle, num_elems, f);
393 0 : break;
394 0 : case 3:
395 0 : glUniform3fv(handle, num_elems, f);
396 0 : break;
397 0 : case 4:
398 0 : glUniform4fv(handle, num_elems, f);
399 0 : break;
400 : default:
401 : ASSERT(false);
402 : break;
403 : }
404 :
405 0 : return check_gl_error(name);
406 : }
407 :
408 0 : static bool glsl_set_shader_bool(ALLEGRO_SHADER *shader,
409 : const char *name, bool b)
410 : {
411 0 : return glsl_set_shader_int(shader, name, b);
412 : }
413 :
414 : static struct ALLEGRO_SHADER_INTERFACE shader_glsl_vt =
415 : {
416 : glsl_attach_shader_source,
417 : glsl_build_shader,
418 : glsl_use_shader,
419 : glsl_unuse_shader,
420 : glsl_destroy_shader,
421 : NULL, /* on_lost_device */
422 : NULL, /* on_reset_device */
423 : glsl_set_shader_sampler,
424 : glsl_set_shader_matrix,
425 : glsl_set_shader_int,
426 : glsl_set_shader_float,
427 : glsl_set_shader_int_vector,
428 : glsl_set_shader_float_vector,
429 : glsl_set_shader_bool
430 : };
431 :
432 0 : static void lookup_varlocs(ALLEGRO_OGL_VARLOCS *varlocs, GLuint program)
433 : {
434 : unsigned i;
435 :
436 0 : varlocs->pos_loc = glGetAttribLocation(program, ALLEGRO_SHADER_VAR_POS);
437 0 : varlocs->color_loc = glGetAttribLocation(program, ALLEGRO_SHADER_VAR_COLOR);
438 0 : varlocs->projview_matrix_loc = glGetUniformLocation(program, ALLEGRO_SHADER_VAR_PROJVIEW_MATRIX);
439 0 : varlocs->texcoord_loc = glGetAttribLocation(program, ALLEGRO_SHADER_VAR_TEXCOORD);
440 0 : varlocs->use_tex_loc = glGetUniformLocation(program, ALLEGRO_SHADER_VAR_USE_TEX);
441 0 : varlocs->tex_loc = glGetUniformLocation(program, ALLEGRO_SHADER_VAR_TEX);
442 0 : varlocs->use_tex_matrix_loc = glGetUniformLocation(program, ALLEGRO_SHADER_VAR_USE_TEX_MATRIX);
443 0 : varlocs->tex_matrix_loc = glGetUniformLocation(program, ALLEGRO_SHADER_VAR_TEX_MATRIX);
444 :
445 0 : for (i = 0; i < _ALLEGRO_PRIM_MAX_USER_ATTR; i++) {
446 : /* al_user_attr_##0 */
447 : char user_attr_name[sizeof(ALLEGRO_SHADER_VAR_USER_ATTR "999")];
448 :
449 0 : snprintf(user_attr_name, sizeof(user_attr_name), ALLEGRO_SHADER_VAR_USER_ATTR "%d", i);
450 0 : varlocs->user_attr_loc[i] = glGetAttribLocation(program, user_attr_name);
451 : }
452 :
453 0 : check_gl_error("glGetAttribLocation, glGetUniformLocation");
454 0 : }
455 :
456 0 : bool _al_glsl_set_projview_matrix(GLint projview_matrix_loc,
457 : const ALLEGRO_TRANSFORM *t)
458 : {
459 0 : if (projview_matrix_loc >= 0) {
460 0 : glUniformMatrix4fv(projview_matrix_loc, 1, false, (float *)t->m);
461 0 : return true;
462 : }
463 :
464 : return false;
465 : }
466 :
467 6 : void _al_glsl_init_shaders(void)
468 : {
469 6 : _al_vector_init(&shaders, sizeof(ALLEGRO_SHADER *));
470 6 : shaders_mutex = al_create_mutex();
471 6 : }
472 :
473 5 : void _al_glsl_shutdown_shaders(void)
474 : {
475 5 : _al_vector_free(&shaders);
476 5 : al_destroy_mutex(shaders_mutex);
477 5 : shaders_mutex = NULL;
478 5 : }
479 :
480 : /* Look through all the bitmaps associated with all the shaders and c;ear their
481 : * shader field */
482 0 : void _al_glsl_unuse_shaders(void)
483 : {
484 : unsigned i;
485 0 : al_lock_mutex(shaders_mutex);
486 0 : for (i = 0; i < _al_vector_size(&shaders); i++) {
487 : unsigned j;
488 0 : ALLEGRO_SHADER *shader = *((ALLEGRO_SHADER **)_al_vector_ref(&shaders, i));
489 :
490 0 : for (j = 0; j < _al_vector_size(&shader->bitmaps); j++) {
491 0 : ALLEGRO_BITMAP *bitmap =
492 0 : *((ALLEGRO_BITMAP **)_al_vector_ref(&shader->bitmaps, j));
493 0 : _al_set_bitmap_shader_field(bitmap, NULL);
494 : }
495 : }
496 0 : al_unlock_mutex(shaders_mutex);
497 0 : }
498 :
499 : #endif
500 :
501 : /* Function: al_get_opengl_program_object
502 : */
503 0 : GLuint al_get_opengl_program_object(ALLEGRO_SHADER *shader)
504 : {
505 : ASSERT(shader);
506 : #ifdef ALLEGRO_CFG_SHADER_GLSL
507 0 : if (shader->platform != ALLEGRO_SHADER_GLSL)
508 : return 0;
509 :
510 0 : return ((ALLEGRO_SHADER_GLSL_S *)shader)->program_object;
511 : #else
512 : return 0;
513 : #endif
514 : }
515 :
516 :
517 : /* vim: set sts=3 sw=3 et: */
|