1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2011-2017 - Daniel De Matteis
3  *
4  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
5  *  of the GNU General Public License as published by the Free Software Found-
6  *  ation, either version 3 of the License, or (at your option) any later version.
7  *
8  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10  *  PURPOSE.  See the GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License along with RetroArch.
13  *  If not, see <http://www.gnu.org/licenses/>.
14  */
15 
16 #include <retro_miscellaneous.h>
17 
18 #ifdef HAVE_CONFIG_H
19 #include "../../config.h"
20 #endif
21 
22 #include "../../retroarch.h"
23 #include "../font_driver.h"
24 #include "../common/gl_common.h"
25 
26 #include "../gfx_display.h"
27 
28 #if defined(__arm__) || defined(__aarch64__)
29 static int scx0, scx1, scy0, scy1;
30 
31 /* This array contains problematic GPU drivers
32  * that have problems when we draw outside the
33  * bounds of the framebuffer */
34 static const struct
35 {
36    const char *str;
37    int len;
38 } scissor_device_strings[] = {
39    { "ARM Mali-4xx", 10 },
40    { 0, 0 }
41 };
42 
scissor_set_rectangle(int x0,int x1,int y0,int y1,int sc)43 static void scissor_set_rectangle(
44       int x0, int x1, int y0, int y1, int sc)
45 {
46    const int dx = sc ? 10 : 2;
47    const int dy = dx;
48    scx0         = x0 + dx;
49    scx1         = x1 - dx;
50    scy0         = y0 + dy;
51    scy1         = y1 - dy;
52 }
53 
scissor_is_outside_rectangle(int x0,int x1,int y0,int y1)54 static bool scissor_is_outside_rectangle(
55       int x0, int x1, int y0, int y1)
56 {
57    if (x1 < scx0)
58       return true;
59    if (scx1 < x0)
60       return true;
61    if (y1 < scy0)
62       return true;
63    if (scy1 < y0)
64       return true;
65    return false;
66 }
67 
68 #define MALI_BUG
69 #endif
70 
71 static const GLfloat gl_vertexes[] = {
72    0, 0,
73    1, 0,
74    0, 1,
75    1, 1
76 };
77 
78 static const GLfloat gl_tex_coords[] = {
79    0, 1,
80    1, 1,
81    0, 0,
82    1, 0
83 };
84 
gfx_display_gl_get_default_vertices(void)85 static const float *gfx_display_gl_get_default_vertices(void)
86 {
87    return &gl_vertexes[0];
88 }
89 
gfx_display_gl_get_default_tex_coords(void)90 static const float *gfx_display_gl_get_default_tex_coords(void)
91 {
92    return &gl_tex_coords[0];
93 }
94 
gfx_display_gl_get_default_mvp(void * data)95 static void *gfx_display_gl_get_default_mvp(void *data)
96 {
97    gl_t *gl = (gl_t*)data;
98 
99    if (!gl)
100       return NULL;
101 
102    return &gl->mvp_no_rot;
103 }
104 
gfx_display_prim_to_gl_enum(enum gfx_display_prim_type type)105 static GLenum gfx_display_prim_to_gl_enum(
106       enum gfx_display_prim_type type)
107 {
108    switch (type)
109    {
110       case GFX_DISPLAY_PRIM_TRIANGLESTRIP:
111          return GL_TRIANGLE_STRIP;
112       case GFX_DISPLAY_PRIM_TRIANGLES:
113          return GL_TRIANGLES;
114       case GFX_DISPLAY_PRIM_NONE:
115       default:
116          break;
117    }
118 
119    return 0;
120 }
121 
gfx_display_gl_blend_begin(void * data)122 static void gfx_display_gl_blend_begin(void *data)
123 {
124    gl_t             *gl          = (gl_t*)data;
125 
126    glEnable(GL_BLEND);
127    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
128 
129    gl->shader->use(gl, gl->shader_data, VIDEO_SHADER_STOCK_BLEND,
130          true);
131 }
132 
gfx_display_gl_blend_end(void * data)133 static void gfx_display_gl_blend_end(void *data)
134 {
135    glDisable(GL_BLEND);
136 }
137 
138 #ifdef MALI_BUG
139 static bool
gfx_display_gl_discard_draw_rectangle(gfx_display_ctx_draw_t * draw,unsigned width,unsigned height)140 gfx_display_gl_discard_draw_rectangle(gfx_display_ctx_draw_t *draw,
141       unsigned width, unsigned height)
142 {
143    static bool mali_4xx_detected     = false;
144    static bool scissor_inited        = false;
145    static unsigned last_video_width  = 0;
146    static unsigned last_video_height = 0;
147 
148    if (!scissor_inited)
149    {
150       unsigned i;
151       const char *gpu_device_string = NULL;
152       scissor_inited                = true;
153 
154       scissor_set_rectangle(0,
155             width - 1,
156             0,
157             height - 1,
158             0);
159 
160       /* TODO/FIXME - This might be thread unsafe in the long run -
161        * preferably call this once outside of the menu display driver
162        * and then just pass this string as a parameter */
163       gpu_device_string = video_driver_get_gpu_device_string();
164 
165       if (gpu_device_string)
166       {
167          for (i = 0; scissor_device_strings[i].len; ++i)
168          {
169             if (strncmp(gpu_device_string,
170                      scissor_device_strings[i].str,
171                      scissor_device_strings[i].len) == 0)
172             {
173                mali_4xx_detected = true;
174                break;
175             }
176          }
177       }
178 
179       last_video_width  = width;
180       last_video_height = height;
181    }
182 
183    /* Early out, to minimise performance impact on
184     * non-mali_4xx devices */
185    if (!mali_4xx_detected)
186       return false;
187 
188    /* Have to update scissor_set_rectangle() if the
189     * video dimensions change */
190    if ((width  != last_video_width) ||
191        (height != last_video_height))
192    {
193       scissor_set_rectangle(0,
194             width - 1,
195             0,
196             height - 1,
197             0);
198 
199       last_video_width  = width;
200       last_video_height = height;
201    }
202 
203    /* Discards not only out-of-bounds scissoring,
204     * but also out-of-view draws.
205     *
206     * This is intentional.
207     */
208    return scissor_is_outside_rectangle(
209          draw->x, draw->x + draw->width - 1,
210          draw->y, draw->y + draw->height - 1);
211 }
212 #endif
213 
gfx_display_gl_draw(gfx_display_ctx_draw_t * draw,void * data,unsigned video_width,unsigned video_height)214 static void gfx_display_gl_draw(gfx_display_ctx_draw_t *draw,
215       void *data, unsigned video_width, unsigned video_height)
216 {
217    gl_t             *gl  = (gl_t*)data;
218 
219    if (!gl || !draw)
220       return;
221 
222 #ifdef MALI_BUG
223    if (gfx_display_gl_discard_draw_rectangle(draw, video_width,
224             video_height))
225    {
226       /*RARCH_WARN("[Menu]: discarded draw rect: %.4i %.4i %.4i %.4i\n",
227         (int)draw->x, (int)draw->y, (int)draw->width, (int)draw->height);*/
228       return;
229    }
230 #endif
231 
232    if (!draw->coords->vertex)
233       draw->coords->vertex        = &gl_vertexes[0];
234    if (!draw->coords->tex_coord)
235       draw->coords->tex_coord     = &gl_tex_coords[0];
236    if (!draw->coords->lut_tex_coord)
237       draw->coords->lut_tex_coord = &gl_tex_coords[0];
238 
239    glViewport(draw->x, draw->y, draw->width, draw->height);
240    glBindTexture(GL_TEXTURE_2D, (GLuint)draw->texture);
241 
242    gl->shader->set_coords(gl->shader_data, draw->coords);
243    gl->shader->set_mvp(gl->shader_data,
244          draw->matrix_data ? (math_matrix_4x4*)draw->matrix_data
245       : (math_matrix_4x4*)&gl->mvp_no_rot);
246 
247 
248    glDrawArrays(gfx_display_prim_to_gl_enum(
249             draw->prim_type), 0, draw->coords->vertices);
250 
251    gl->coords.color     = gl->white_color_ptr;
252 }
253 
gfx_display_gl_draw_pipeline(gfx_display_ctx_draw_t * draw,gfx_display_t * p_disp,void * data,unsigned video_width,unsigned video_height)254 static void gfx_display_gl_draw_pipeline(
255       gfx_display_ctx_draw_t *draw,
256       gfx_display_t *p_disp,
257       void *data,
258       unsigned video_width,
259       unsigned video_height)
260 {
261 #ifdef HAVE_SHADERPIPELINE
262    struct uniform_info uniform_param;
263    gl_t             *gl             = (gl_t*)data;
264    static float t                   = 0;
265    video_coord_array_t *ca          = &p_disp->dispca;
266 
267    draw->x                          = 0;
268    draw->y                          = 0;
269    draw->coords                     = (struct video_coords*)(&ca->coords);
270    draw->matrix_data                = NULL;
271 
272    switch (draw->pipeline_id)
273    {
274       case VIDEO_SHADER_MENU:
275       case VIDEO_SHADER_MENU_2:
276          glBlendFunc(GL_ONE, GL_ONE);
277          break;
278       default:
279          glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
280          break;
281    }
282 
283    switch (draw->pipeline_id)
284    {
285       case VIDEO_SHADER_MENU:
286       case VIDEO_SHADER_MENU_2:
287       case VIDEO_SHADER_MENU_3:
288       case VIDEO_SHADER_MENU_4:
289       case VIDEO_SHADER_MENU_5:
290       case VIDEO_SHADER_MENU_6:
291          gl->shader->use(gl, gl->shader_data, draw->pipeline_id,
292                true);
293 
294          t += 0.01;
295 
296          uniform_param.type              = UNIFORM_1F;
297          uniform_param.enabled           = true;
298          uniform_param.location          = 0;
299          uniform_param.count             = 0;
300 
301          uniform_param.lookup.type       = SHADER_PROGRAM_VERTEX;
302          uniform_param.lookup.ident      = "time";
303          uniform_param.lookup.idx        = draw->pipeline_id;
304          uniform_param.lookup.add_prefix = true;
305          uniform_param.lookup.enable     = true;
306 
307          uniform_param.result.f.v0       = t;
308 
309          gl->shader->set_uniform_parameter(gl->shader_data,
310                &uniform_param, NULL);
311          break;
312    }
313 
314    switch (draw->pipeline_id)
315    {
316       case VIDEO_SHADER_MENU_3:
317       case VIDEO_SHADER_MENU_4:
318       case VIDEO_SHADER_MENU_5:
319       case VIDEO_SHADER_MENU_6:
320 #ifndef HAVE_PSGL
321          uniform_param.type              = UNIFORM_2F;
322          uniform_param.lookup.ident      = "OutputSize";
323          uniform_param.result.f.v0       = draw->width;
324          uniform_param.result.f.v1       = draw->height;
325 
326          gl->shader->set_uniform_parameter(gl->shader_data,
327                &uniform_param, NULL);
328 #endif
329          break;
330    }
331 #endif
332 }
333 
gfx_display_gl_font_init_first(void ** font_handle,void * video_data,const char * font_path,float menu_font_size,bool is_threaded)334 static bool gfx_display_gl_font_init_first(
335       void **font_handle, void *video_data,
336       const char *font_path, float menu_font_size,
337       bool is_threaded)
338 {
339    font_data_t **handle = (font_data_t**)font_handle;
340    if (!(*handle = font_driver_init_first(video_data,
341          font_path, menu_font_size, true,
342          is_threaded,
343          FONT_DRIVER_RENDER_OPENGL_API)))
344 		 return false;
345    return true;
346 }
347 
gfx_display_gl_scissor_begin(void * data,unsigned video_width,unsigned video_height,int x,int y,unsigned width,unsigned height)348 static void gfx_display_gl_scissor_begin(
349       void *data,
350       unsigned video_width,
351       unsigned video_height,
352       int x, int y,
353       unsigned width, unsigned height)
354 {
355    glScissor(x, video_height - y - height, width, height);
356    glEnable(GL_SCISSOR_TEST);
357 #ifdef MALI_BUG
358    /* TODO/FIXME: If video width/height changes between
359     * a call of gfx_display_gl_scissor_begin() and the
360     * next call of gfx_display_gl_draw() (or if
361     * gfx_display_gl_scissor_begin() is called before the
362     * first call of gfx_display_gl_draw()), the scissor
363     * rectangle set here will be overwritten by the initialisation
364     * procedure inside gfx_display_gl_discard_draw_rectangle(),
365     * causing the next frame to render glitched content */
366    scissor_set_rectangle(x, x + width - 1, y, y + height - 1, 1);
367 #endif
368 }
369 
gfx_display_gl_scissor_end(void * data,unsigned video_width,unsigned video_height)370 static void gfx_display_gl_scissor_end(
371       void *data,
372       unsigned video_width,
373       unsigned video_height)
374 {
375    glScissor(0, 0, video_width, video_height);
376    glDisable(GL_SCISSOR_TEST);
377 #ifdef MALI_BUG
378    scissor_set_rectangle(0, video_width - 1, 0, video_height - 1, 0);
379 #endif
380 }
381 
382 gfx_display_ctx_driver_t gfx_display_ctx_gl = {
383    gfx_display_gl_draw,
384    gfx_display_gl_draw_pipeline,
385    gfx_display_gl_blend_begin,
386    gfx_display_gl_blend_end,
387    gfx_display_gl_get_default_mvp,
388    gfx_display_gl_get_default_vertices,
389    gfx_display_gl_get_default_tex_coords,
390    gfx_display_gl_font_init_first,
391    GFX_VIDEO_DRIVER_OPENGL,
392    "gl",
393    false,
394    gfx_display_gl_scissor_begin,
395    gfx_display_gl_scissor_end
396 };
397