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