1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      Shader API.
12  *
13  *      See LICENSE.txt for copyright information.
14  */
15 
16 #include "allegro5/allegro.h"
17 #include "allegro5/internal/aintern_bitmap.h"
18 #include "allegro5/internal/aintern_display.h"
19 #include "allegro5/internal/aintern_dtor.h"
20 #include "allegro5/internal/aintern_shader.h"
21 #include "allegro5/internal/aintern_system.h"
22 
23 #ifdef ALLEGRO_CFG_SHADER_GLSL
24 #include "allegro5/allegro_opengl.h"
25 #endif
26 
27 ALLEGRO_DEBUG_CHANNEL("shader")
28 
29 #include "shader_source.inc"
30 
31 
resolve_platform(ALLEGRO_SHADER_PLATFORM platform)32 static ALLEGRO_SHADER_PLATFORM resolve_platform(ALLEGRO_SHADER_PLATFORM platform)
33 {
34    if (platform == ALLEGRO_SHADER_AUTO) {
35       ALLEGRO_DISPLAY *display = al_get_current_display();
36       ASSERT(display);
37       if (al_get_display_flags(display) & ALLEGRO_OPENGL) {
38          platform = ALLEGRO_SHADER_GLSL;
39       }
40       else {
41          platform = ALLEGRO_SHADER_HLSL;
42       }
43    }
44 
45    return platform;
46 }
47 
48 /* Function: al_create_shader
49  */
al_create_shader(ALLEGRO_SHADER_PLATFORM platform)50 ALLEGRO_SHADER *al_create_shader(ALLEGRO_SHADER_PLATFORM platform)
51 {
52    ALLEGRO_SHADER *shader = NULL;
53 
54    platform = resolve_platform(platform);
55 
56    if (false) {
57    }
58 #ifdef ALLEGRO_CFG_SHADER_GLSL
59    else if (platform == ALLEGRO_SHADER_GLSL) {
60       shader = _al_create_shader_glsl(platform);
61    }
62 #endif
63 #ifdef ALLEGRO_CFG_SHADER_HLSL
64    else if (platform == ALLEGRO_SHADER_HLSL) {
65       shader = _al_create_shader_hlsl(platform);
66    }
67 #endif
68 
69    if (shader) {
70       ASSERT(shader->platform);
71       ASSERT(shader->vt);
72       shader->dtor_item = _al_register_destructor(_al_dtor_list, "shader", shader,
73          (void (*)(void *))al_destroy_shader);
74    }
75    else {
76       ALLEGRO_WARN("Failed to create shader\n");
77    }
78    return shader;
79 }
80 
81 /* Function: al_attach_shader_source
82  */
al_attach_shader_source(ALLEGRO_SHADER * shader,ALLEGRO_SHADER_TYPE type,const char * source)83 bool al_attach_shader_source(ALLEGRO_SHADER *shader, ALLEGRO_SHADER_TYPE type,
84     const char *source)
85 {
86    ASSERT(shader);
87    return shader->vt->attach_shader_source(shader, type, source);
88 }
89 
90 /* Function: al_attach_shader_source_file
91  */
al_attach_shader_source_file(ALLEGRO_SHADER * shader,ALLEGRO_SHADER_TYPE type,const char * filename)92 bool al_attach_shader_source_file(ALLEGRO_SHADER *shader,
93    ALLEGRO_SHADER_TYPE type, const char *filename)
94 {
95    ALLEGRO_FILE *fp;
96    ALLEGRO_USTR *str;
97    bool ret;
98 
99    fp = al_fopen(filename, "r");
100    if (!fp) {
101       ALLEGRO_WARN("Failed to open %s\n", filename);
102       al_ustr_free(shader->log);
103       shader->log = al_ustr_newf("Failed to open %s", filename);
104       return false;
105    }
106    str = al_ustr_new("");
107    for (;;) {
108       char buf[512];
109       size_t n;
110       ALLEGRO_USTR_INFO info;
111 
112       n = al_fread(fp, buf, sizeof(buf));
113       if (n <= 0)
114          break;
115       al_ustr_append(str, al_ref_buffer(&info, buf, n));
116    }
117    al_fclose(fp);
118    ret = al_attach_shader_source(shader, type, al_cstr(str));
119    al_ustr_free(str);
120    return ret;
121 }
122 
123 /* Function: al_build_shader
124  */
al_build_shader(ALLEGRO_SHADER * shader)125 bool al_build_shader(ALLEGRO_SHADER *shader)
126 {
127    ASSERT(shader);
128    return shader->vt->build_shader(shader);
129 }
130 
131 /* Function: al_get_shader_log
132  */
al_get_shader_log(ALLEGRO_SHADER * shader)133 const char *al_get_shader_log(ALLEGRO_SHADER *shader)
134 {
135    ASSERT(shader);
136 
137    return (shader->log) ? al_cstr(shader->log) : "";
138 }
139 
140 /* Function: al_get_shader_platform
141  */
al_get_shader_platform(ALLEGRO_SHADER * shader)142 ALLEGRO_SHADER_PLATFORM al_get_shader_platform(ALLEGRO_SHADER *shader)
143 {
144    ASSERT(shader);
145    return shader->platform;
146 }
147 
148 /* Function: al_use_shader
149  */
al_use_shader(ALLEGRO_SHADER * shader)150 bool al_use_shader(ALLEGRO_SHADER *shader)
151 {
152    ALLEGRO_BITMAP *bmp = al_get_target_bitmap();
153    ALLEGRO_DISPLAY *disp;
154 
155    if (!bmp) {
156       ALLEGRO_WARN("No current target bitmap.\n");
157       return false;
158    }
159    if (al_get_bitmap_flags(bmp) & ALLEGRO_MEMORY_BITMAP) {
160       ALLEGRO_WARN("Target bitmap is memory bitmap.\n");
161       return false;
162    }
163    disp = _al_get_bitmap_display(bmp);
164    ASSERT(disp);
165 
166    if (shader) {
167       if (shader->vt->use_shader(shader, disp, true)) {
168          _al_set_bitmap_shader_field(bmp, shader);
169          ALLEGRO_DEBUG("use_shader succeeded\n");
170          return true;
171       }
172       else {
173          _al_set_bitmap_shader_field(bmp, NULL);
174          ALLEGRO_ERROR("use_shader failed\n");
175          if (disp->default_shader) {
176             disp->default_shader->vt->use_shader(
177                disp->default_shader, disp, true);
178          }
179          return false;
180       }
181    }
182    else {
183       if (bmp->shader) {
184          bmp->shader->vt->unuse_shader(bmp->shader, disp);
185          _al_set_bitmap_shader_field(bmp, NULL);
186       }
187       if (disp->default_shader) {
188          disp->default_shader->vt->use_shader(
189             disp->default_shader, disp, true);
190       }
191       return true;
192    }
193 }
194 
195 /* Function: al_destroy_shader
196  */
al_destroy_shader(ALLEGRO_SHADER * shader)197 void al_destroy_shader(ALLEGRO_SHADER *shader)
198 {
199    ALLEGRO_BITMAP *bmp;
200    unsigned i;
201 
202    if (!shader)
203       return;
204 
205    /* As a convenience, implicitly unuse the shader on the target bitmap
206     * if currently used.
207     */
208    bmp = al_get_target_bitmap();
209    if (bmp && _al_vector_contains(&shader->bitmaps, &bmp)) {
210       ALLEGRO_DEBUG("implicitly unusing shader on target bitmap\n");
211       al_use_shader(NULL);
212    }
213 
214    _al_unregister_destructor(_al_dtor_list, shader->dtor_item);
215 
216    al_ustr_free(shader->vertex_copy);
217    shader->vertex_copy = NULL;
218    al_ustr_free(shader->pixel_copy);
219    shader->pixel_copy = NULL;
220    al_ustr_free(shader->log);
221    shader->log = NULL;
222 
223    /* Clear references to this shader from all bitmaps. */
224    for (i = 0; i < _al_vector_size(&shader->bitmaps); i++) {
225       ALLEGRO_BITMAP **slot = _al_vector_ref(&shader->bitmaps, i);
226       ALLEGRO_BITMAP *bitmap = *slot;
227       ASSERT(bitmap->shader == shader);
228       bitmap->shader = NULL;
229    }
230    _al_vector_free(&shader->bitmaps);
231 
232    shader->vt->destroy_shader(shader);
233 }
234 
235 /* Function: al_set_shader_sampler
236  */
al_set_shader_sampler(const char * name,ALLEGRO_BITMAP * bitmap,int unit)237 bool al_set_shader_sampler(const char *name,
238    ALLEGRO_BITMAP *bitmap, int unit)
239 {
240    ALLEGRO_BITMAP *bmp;
241    ALLEGRO_SHADER *shader;
242 
243    if ((bmp = al_get_target_bitmap()) != NULL) {
244       if ((shader = bmp->shader) != NULL) {
245          return shader->vt->set_shader_sampler(shader, name, bitmap, unit);
246       }
247       else {
248          return false;
249       }
250    }
251    else {
252       return false;
253    }
254 }
255 
256 /* Function: al_set_shader_matrix
257  */
al_set_shader_matrix(const char * name,const ALLEGRO_TRANSFORM * matrix)258 bool al_set_shader_matrix(const char *name,
259    const ALLEGRO_TRANSFORM *matrix)
260 {
261    ALLEGRO_BITMAP *bmp;
262    ALLEGRO_SHADER *shader;
263 
264    if ((bmp = al_get_target_bitmap()) != NULL) {
265       if ((shader = bmp->shader) != NULL) {
266          return shader->vt->set_shader_matrix(shader, name, matrix);
267       }
268       else {
269          return false;
270       }
271    }
272    else {
273       return false;
274    }
275 }
276 
277 /* Function: al_set_shader_int
278  */
al_set_shader_int(const char * name,int i)279 bool al_set_shader_int(const char *name, int i)
280 {
281    ALLEGRO_BITMAP *bmp;
282    ALLEGRO_SHADER *shader;
283 
284    if ((bmp = al_get_target_bitmap()) != NULL) {
285       if ((shader = bmp->shader) != NULL) {
286          return shader->vt->set_shader_int(shader, name, i);
287       }
288       else {
289          return false;
290       }
291    }
292    else {
293       return false;
294    }
295 }
296 
297 /* Function: al_set_shader_float
298  */
al_set_shader_float(const char * name,float f)299 bool al_set_shader_float(const char *name, float f)
300 {
301    ALLEGRO_BITMAP *bmp;
302    ALLEGRO_SHADER *shader;
303 
304    if ((bmp = al_get_target_bitmap()) != NULL) {
305       if ((shader = bmp->shader) != NULL) {
306          return shader->vt->set_shader_float(shader, name, f);
307       }
308       else {
309          return false;
310       }
311    }
312    else {
313       return false;
314    }
315 }
316 
317 /* Function: al_set_shader_int_vector
318  */
al_set_shader_int_vector(const char * name,int num_components,const int * i,int num_elems)319 bool al_set_shader_int_vector(const char *name,
320    int num_components, const int *i, int num_elems)
321 {
322    ALLEGRO_BITMAP *bmp;
323    ALLEGRO_SHADER *shader;
324 
325    if ((bmp = al_get_target_bitmap()) != NULL) {
326       if ((shader = bmp->shader) != NULL) {
327          return shader->vt->set_shader_int_vector(shader, name, num_components, i, num_elems);
328       }
329       else {
330          return false;
331       }
332    }
333    else {
334       return false;
335    }
336 }
337 
338 /* Function: al_set_shader_float_vector
339  */
al_set_shader_float_vector(const char * name,int num_components,const float * f,int num_elems)340 bool al_set_shader_float_vector(const char *name,
341    int num_components, const float *f, int num_elems)
342 {
343    ALLEGRO_BITMAP *bmp;
344    ALLEGRO_SHADER *shader;
345 
346    if ((bmp = al_get_target_bitmap()) != NULL) {
347       if ((shader = bmp->shader) != NULL) {
348          return shader->vt->set_shader_float_vector(shader, name, num_components, f, num_elems);
349       }
350       else {
351          return false;
352       }
353    }
354    else {
355       return false;
356    }
357 }
358 
359 /* Function: al_set_shader_bool
360  */
al_set_shader_bool(const char * name,bool b)361 bool al_set_shader_bool(const char *name, bool b)
362 {
363    ALLEGRO_BITMAP *bmp;
364    ALLEGRO_SHADER *shader;
365 
366    if ((bmp = al_get_target_bitmap()) != NULL) {
367       if ((shader = bmp->shader) != NULL) {
368          return shader->vt->set_shader_bool(shader, name, b);
369       }
370       else {
371          return false;
372       }
373    }
374    else {
375       return false;
376    }
377 }
378 
379 /* Function: al_get_default_shader_source
380  */
al_get_default_shader_source(ALLEGRO_SHADER_PLATFORM platform,ALLEGRO_SHADER_TYPE type)381 char const *al_get_default_shader_source(ALLEGRO_SHADER_PLATFORM platform,
382    ALLEGRO_SHADER_TYPE type)
383 {
384    (void)type;
385    switch (resolve_platform(platform)) {
386       case ALLEGRO_SHADER_GLSL:
387 #ifdef ALLEGRO_CFG_SHADER_GLSL
388          switch (type) {
389             case ALLEGRO_VERTEX_SHADER:
390                return default_glsl_vertex_source;
391             case ALLEGRO_PIXEL_SHADER:
392                return default_glsl_pixel_source;
393          }
394 #endif
395          break;
396 
397       case ALLEGRO_SHADER_HLSL:
398 #ifdef ALLEGRO_CFG_SHADER_HLSL
399          switch (type) {
400             case ALLEGRO_VERTEX_SHADER:
401                return default_hlsl_vertex_source;
402             case ALLEGRO_PIXEL_SHADER:
403                return default_hlsl_pixel_source;
404          }
405 #endif
406          break;
407 
408       case ALLEGRO_SHADER_AUTO:
409          ASSERT(0);
410    }
411    return NULL;
412 }
413 
_al_set_bitmap_shader_field(ALLEGRO_BITMAP * bmp,ALLEGRO_SHADER * shader)414 void _al_set_bitmap_shader_field(ALLEGRO_BITMAP *bmp, ALLEGRO_SHADER *shader)
415 {
416    ASSERT(bmp);
417 
418    if (bmp->shader != shader) {
419       if (bmp->shader) {
420          _al_unregister_shader_bitmap(bmp->shader, bmp);
421       }
422       bmp->shader = shader;
423       if (bmp->shader) {
424          _al_register_shader_bitmap(bmp->shader, bmp);
425       }
426    }
427 }
428 
_al_register_shader_bitmap(ALLEGRO_SHADER * shader,ALLEGRO_BITMAP * bmp)429 void _al_register_shader_bitmap(ALLEGRO_SHADER *shader, ALLEGRO_BITMAP *bmp)
430 {
431    ALLEGRO_BITMAP **slot;
432    ASSERT(shader);
433    ASSERT(bmp);
434 
435    slot = _al_vector_alloc_back(&shader->bitmaps);
436    *slot = bmp;
437 }
438 
_al_unregister_shader_bitmap(ALLEGRO_SHADER * shader,ALLEGRO_BITMAP * bmp)439 void _al_unregister_shader_bitmap(ALLEGRO_SHADER *shader, ALLEGRO_BITMAP *bmp)
440 {
441    bool deleted;
442    ASSERT(shader);
443    ASSERT(bmp);
444 
445    deleted = _al_vector_find_and_delete(&shader->bitmaps, &bmp);
446    ASSERT(deleted);
447 }
448 
_al_create_default_shader(int display_flags)449 ALLEGRO_SHADER *_al_create_default_shader(int display_flags)
450 {
451    ALLEGRO_SHADER_PLATFORM platform = ALLEGRO_SHADER_AUTO;
452    ALLEGRO_SHADER *shader;
453    (void)display_flags;
454 
455    if (false) {
456    }
457 #ifdef ALLEGRO_CFG_SHADER_GLSL
458    else if (display_flags & ALLEGRO_OPENGL) {
459       platform = ALLEGRO_SHADER_GLSL;
460    }
461 #endif
462 #ifdef ALLEGRO_CFG_SHADER_HLSL
463    else if (display_flags & ALLEGRO_DIRECT3D_INTERNAL) {
464       platform = ALLEGRO_SHADER_HLSL;
465    }
466 #endif
467 
468    if (platform == ALLEGRO_SHADER_AUTO) {
469       ALLEGRO_ERROR("No suitable shader platform found for creating the default shader.\n");
470       return false;
471    }
472 
473    _al_push_destructor_owner();
474    shader = al_create_shader(platform);
475    _al_pop_destructor_owner();
476 
477    if (!shader) {
478       ALLEGRO_ERROR("Error creating default shader.\n");
479       return false;
480    }
481    if (!al_attach_shader_source(shader, ALLEGRO_VERTEX_SHADER,
482          al_get_default_shader_source(platform, ALLEGRO_VERTEX_SHADER))) {
483       ALLEGRO_ERROR("al_attach_shader_source for vertex shader failed: %s\n",
484          al_get_shader_log(shader));
485       goto fail;
486    }
487    if (!al_attach_shader_source(shader, ALLEGRO_PIXEL_SHADER,
488          al_get_default_shader_source(platform, ALLEGRO_PIXEL_SHADER))) {
489       ALLEGRO_ERROR("al_attach_shader_source for pixel shader failed: %s\n",
490          al_get_shader_log(shader));
491       goto fail;
492    }
493    if (!al_build_shader(shader)) {
494       ALLEGRO_ERROR("al_build_shader failed: %s\n", al_get_shader_log(shader));
495       goto fail;
496    }
497    return shader;
498 
499 fail:
500    al_destroy_shader(shader);
501    return NULL;
502 }
503 
504 /* vim: set sts=3 sw=3 et: */
505