1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 
5 #define FSE_INTERNAL_API
6 #include <fs/emu.h>
7 #include <fs/emu/audio.h>
8 #include <fs/emu/options.h>
9 #include <fs/emu/path.h>
10 #include <fs/emu/render.h>
11 #include <fs/emu/video.h>
12 
13 #include "render.h"
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 
19 #include <fs/conf.h>
20 #include <fs/filesys.h>
21 #include <fs/i18n.h>
22 #include <fs/glib.h>
23 #include <fs/ml.h>
24 #include <fs/thread.h>
25 #include <fs/time.h>
26 #include <fs/util.h>
27 
28 #include "libfsemu.h"
29 #include "audio.h"
30 #include "dialog.h"
31 #include "emu_lua.h"
32 #include "font.h"
33 #include "hud.h"
34 #include "menu.h"
35 #include "scanlines.h"
36 #include "texture.h"
37 #include "theme.h"
38 #include "util.h"
39 #include "video.h"
40 #include "video_buffer.h"
41 #include "xml_shader.h"
42 
43 #ifdef USE_OPENGL
44 #include <fs/ml/opengl.h>
45 #endif
46 
47 #ifdef USE_GLES
48 #define glScaled glScalef
49 #define glTranslated glTranslatef
50 #define glRotated glRotatef
51 #define double float
52 #endif
53 
54 #define VIDEO_DEBUG_SCALE_TIMES 2.5
55 
56 #define FLAT 1
57 
58 // menu transition
59 static double g_menu_transition = 0.0;
60 static double g_menu_transition_target = 0.0;
61 
62 //static fs_emu_texture g_frame_texture = {};
63 static GLuint g_frame_texture = 0;
64 static int g_frame_texture_width = 0;
65 static int g_frame_texture_height = 0;
66 
67 static int g_texture_filter = GL_LINEAR;
68 
69 static int g_frame_texture_black_left = 100000;
70 static int g_frame_texture_black_top = 100000;
71 
72 // crop coordinates of emulator video frame
73 static fs_emu_rect g_crop = {};
74 
75 #define FS_EMU_VIEWPORT_MODE_CROP 0
76 #define FS_EMU_VIEWPORT_MODE_CENTER 1
77 static int g_viewport_mode = FS_EMU_VIEWPORT_MODE_CROP;
78 static int g_effective_viewport_mode = FS_EMU_VIEWPORT_MODE_CROP;
79 
80 // size of emulator video frame
81 static int g_frame_width = 0;
82 static int g_frame_height = 0;
83 
84 static double g_frame_aspect = 0;
85 
86 static uint8_t *g_scanline_buffer = NULL;
87 static int g_scanline_buffer_width = -1;
88 static int g_scanline_buffer_height = -1;
89 
90 // scaling and alignment options
91 static double g_scale_x = -1.0;
92 static double g_scale_y = -1.0;
93 static double g_align_x = 0.5;
94 static double g_align_y = 0.5;
95 
96 static int g_frame_override = 0;
97 static int g_frame_override_x = 0;
98 static int g_frame_override_y = 0;
99 static int g_frame_override_w = 0;
100 static int g_frame_override_h = 0;
101 static int g_l_scale_x = 1920;
102 static int g_l_scale_y = 1080;
103 
104 struct overlay_status {
105     int state;
106     int render_state;
107     //int was_enabled;
108     //int was_disabled;
109     int render_status;
110 };
111 static struct overlay_status g_overlay_status[FS_EMU_MAX_OVERLAYS];
112 static fs_mutex *g_overlay_mutex = NULL;
113 
fs_emu_set_custom_overlay_state(int overlay,int state)114 void fs_emu_set_custom_overlay_state(int overlay, int state)
115 {
116     fs_emu_set_overlay_state(FS_EMU_FIRST_CUSTOM_OVERLAY + overlay, state);
117 }
118 
fs_emu_set_overlay_state(int overlay,int state)119 void fs_emu_set_overlay_state(int overlay, int state)
120 {
121     if (overlay < 0 || overlay >= FS_EMU_MAX_OVERLAYS) {
122         return;
123     }
124     // fs_mutex_lock(g_overlay_mutex);
125     g_overlay_status[overlay].state = state;
126 
127 #if 0
128     if (state) {
129         g_overlay_status[overlay].state = 1;
130         g_overlay_status[overlay].was_enabled = 1;
131     }
132     else {
133         g_overlay_status[overlay].state = 0;
134         g_overlay_status[overlay].was_disabled = 1;
135     }
136 #endif
137     // fs_mutex_unlock(g_overlay_mutex);
138 }
139 
setup_opengl(void)140 static void setup_opengl(void)
141 {
142     fs_log("setup_opengl\n");
143     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
144     CHECK_GL_ERROR();
145 }
146 
context_notification_handler(int notification,void * data)147 static void context_notification_handler(int notification, void *data)
148 {
149     if (notification == FS_GL_CONTEXT_DESTROY) {
150         if (g_frame_texture != 0) {
151             glDeleteTextures(1, &g_frame_texture);
152             CHECK_GL_ERROR();
153             g_frame_texture = 0;
154         }
155     }
156     else if (notification == FS_GL_CONTEXT_CREATE) {
157         setup_opengl();
158     }
159 }
160 
fs_emu_initialize_opengl()161 void fs_emu_initialize_opengl()
162 {
163     setup_opengl();
164     fs_emu_initialize_textures();
165     fs_gl_add_context_notification(context_notification_handler, NULL);
166 }
167 
get_buffer_format(int * gl_buffer_format,int * gl_buffer_type)168 static void get_buffer_format(int *gl_buffer_format, int *gl_buffer_type)
169 {
170     int format = fs_emu_get_video_format();
171     if (format == FS_EMU_VIDEO_FORMAT_BGRA) {
172         *gl_buffer_format = GL_BGRA;
173 #ifdef WORDS_BIGENDIAN
174         *gl_buffer_type = GL_UNSIGNED_INT_8_8_8_8_REV;
175 #else
176         *gl_buffer_type = GL_UNSIGNED_BYTE;
177 #endif
178     }
179     else if (format == FS_EMU_VIDEO_FORMAT_RGBA) {
180         *gl_buffer_format = GL_RGBA;
181 #ifdef WORDS_BIGENDIAN
182         *gl_buffer_type = GL_UNSIGNED_INT_8_8_8_8_REV;
183 #else
184         *gl_buffer_type = GL_UNSIGNED_BYTE;
185 #endif
186     }
187     else if (format == FS_EMU_VIDEO_FORMAT_RGB) {
188         *gl_buffer_format = GL_RGB;
189 #ifdef WORDS_BIGENDIAN
190         *gl_buffer_type = GL_UNSIGNED_INT_8_8_8_8_REV;
191 #else
192         *gl_buffer_type = GL_UNSIGNED_BYTE;
193 #endif
194     }
195     else if (format == FS_EMU_VIDEO_FORMAT_R5G6B5) {
196         *gl_buffer_format = GL_RGB;
197         *gl_buffer_type = GL_UNSIGNED_SHORT_5_6_5;
198     }
199     else if (format == FS_EMU_VIDEO_FORMAT_R5G5B5A1) {
200         *gl_buffer_format = GL_RGBA;
201         *gl_buffer_type = GL_UNSIGNED_SHORT_5_5_5_1;
202     }
203 }
204 
create_texture_if_needed(int width,int height)205 static void create_texture_if_needed(int width, int height)
206 {
207     //g_frame_texture.video_version = g_fs_emu_video_version;
208     if (g_frame_texture && g_frame_texture_width >= width &&
209             g_frame_texture_height >= height) {
210         return;
211     }
212     fs_gl_bind_texture(0);
213 
214     if (g_frame_texture) {
215         glDeleteTextures(1, &g_frame_texture);
216         CHECK_GL_ERROR();
217     }
218     g_frame_texture_width = 1;
219     while (g_frame_texture_width < width) {
220         g_frame_texture_width *= 2;
221     }
222     g_frame_texture_height = 1;
223     while (g_frame_texture_height < height) {
224         g_frame_texture_height *= 2;
225     }
226     glGenTextures(1, &g_frame_texture);
227     CHECK_GL_ERROR();
228     //g_frame_texture.opengl_context_stamp = g_fs_ml_opengl_context_stamp;
229     //fs_emu_set_texture(&g_frame_texture);
230     fs_gl_bind_texture(g_frame_texture);
231 #if 0
232     // with high quality borders, there should be no reason to initialize
233     // the texture to black
234     void *data = g_malloc0(g_frame_texture_width * g_frame_texture_height * 4);
235 #endif
236     void *data = NULL;
237     fs_gl_unpack_row_length(0);
238     int gl_buffer_format = 0;
239     int gl_buffer_type = 0;
240     get_buffer_format(&gl_buffer_format, &gl_buffer_type);
241     glTexImage2D(GL_TEXTURE_2D, 0, fs_emu_get_texture_format(),
242             g_frame_texture_width, g_frame_texture_height, 0,
243             gl_buffer_format, gl_buffer_type, data);
244     CHECK_GL_ERROR();
245 #if 0
246     if (data) {
247         free(data);
248     }
249 #endif
250     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, g_texture_filter);
251     CHECK_GL_ERROR();
252     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, g_texture_filter);
253     CHECK_GL_ERROR();
254     //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
255     //CHECK_GL_ERROR();
256     //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
257     //CHECK_GL_ERROR();
258 #ifdef USE_GLES
259 
260 #else
261     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
262     CHECK_GL_ERROR();
263     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
264     CHECK_GL_ERROR();
265 #endif
266 
267     // texture contains no data now, so we ensure that the upload routine
268     // blanks the border if necessary
269     g_frame_texture_black_left = 100000;
270     g_frame_texture_black_top = 100000;
271 }
272 
fix_border(fs_emu_video_buffer * buffer,int * upload_x,int * upload_y,int * upload_w,int * upload_h)273 static void fix_border(fs_emu_video_buffer *buffer, int *upload_x,
274         int *upload_y, int *upload_w, int *upload_h)
275 {
276     // upload fixed border as well
277     int ux = g_crop.x;
278     int uy = g_crop.y;
279     int uw = g_crop.w;
280     int uh = g_crop.h;
281 
282     // currently disabled border fixing ... -because this "destroys" the
283     // buffer so it cannot be copied properly from when reusing lines
284     // for the next frame
285 #if 0
286     int32_t *idata = (int32_t*) buffer->data;
287     int32_t *src;
288     int32_t *dst;
289 
290     if (uy > 1) {
291         src = idata + uy * buffer->width + ux;
292         uy--;
293         dst = idata + uy * buffer->width + ux;
294         for (int x = 0; x < uw; x++) {
295             *dst++ = *src++;
296         }
297         uh++;
298     }
299     //printf("%d %d %d\n", uy, uh, buffer->buffer_height);
300     if (uy + uh < buffer->buffer_height) {
301         src = idata + (uy + uh - 1) * buffer->width + ux;
302         uh++;
303         dst = idata + (uy + uh - 1) * buffer->width + ux;
304         for (int x = 0; x < uw; x++) {
305             *dst++ = *src++;
306         }
307     }
308     if (ux > 1) {
309         src = idata + uy * buffer->width + ux;
310         ux--;
311         dst = idata + uy * buffer->width + ux;
312         for (int y = 0; y < uh; y++) {
313             *dst = *src;
314             src += buffer->width;
315             dst += buffer->width;
316         }
317         uw++;
318     }
319     if (ux + uw < buffer->buffer_width) {
320         src = idata + uy * buffer->width + (ux + uw - 1);
321         uw++;
322         dst = idata + uy * buffer->width + (ux + uw - 1);
323         for (int y = 0; y < uh; y++) {
324             *dst = *src;
325             src += buffer->width;
326             dst += buffer->width;
327         }
328     }
329 #endif
330     *upload_x = ux;
331     *upload_y = uy;
332     *upload_w = uw;
333     *upload_h = uh;
334 }
335 
336 #define R5G6B5_MASK_R 0xf800
337 #define R5G6B5_MASK_G 0x07e0
338 #define R5G6B5_MASK_B 0x001f
339 #define R5G6B5_SHIFT_R 11
340 #define R5G6B5_SHIFT_G 5
341 #define R5G6B5_SHIFT_B 0
342 
343 #define R5G5B5A1_MASK_R 0xf800
344 #define R5G5B5A1_MASK_G 0x07c0
345 #define R5G5B5A1_MASK_B 0x003e
346 #define R5G5B5A1_SHIFT_R 11
347 #define R5G5B5A1_SHIFT_G 6
348 #define R5G5B5A1_SHIFT_B 1
349 
save_screenshot(const char * path,int cx,int cy,int cw,int ch,int count,uint8_t * frame,int frame_width,int frame_height,int frame_bpp)350 static void save_screenshot(const char *path, int cx, int cy, int cw, int ch,
351         int count, uint8_t *frame, int frame_width, int frame_height,
352         int frame_bpp)
353 {
354     fs_log("writing screenshot to %s\n", path);
355 
356     uint8_t *out_data = malloc(cw * ch * 3);
357     int row_len = cw * frame_bpp;
358     int frame_format = fs_emu_get_video_format();
359 
360     for (int y = 0; y < ch; y++) {
361         uint8_t *ip = frame + ((cy + y) * frame_width + cx) * frame_bpp;
362         uint8_t *op = out_data + y * cw * 3;
363         if (frame_format == FS_EMU_VIDEO_FORMAT_BGRA) {
364             for (int x = 0; x < row_len; x += frame_bpp) {
365 #ifdef WORDS_BIGENDIAN
366                 *op++ = ip[x + 1];
367                 *op++ = ip[x + 2];
368                 *op++ = ip[x + 3];
369 #else
370                 *op++ = ip[x + 2];
371                 *op++ = ip[x + 1];
372                 *op++ = ip[x + 0];
373 #endif
374             }
375         }
376         else if (frame_format == FS_EMU_VIDEO_FORMAT_RGBA) {
377             for (int x = 0; x < row_len; x += frame_bpp) {
378                 *op++ = ip[x + 0];
379                 *op++ = ip[x + 1];
380                 *op++ = ip[x + 2];
381             }
382         }
383         else if (frame_format == FS_EMU_VIDEO_FORMAT_R5G6B5) {
384             for (int x = 0; x < row_len; x += frame_bpp) {
385                 unsigned short *p = (unsigned short *) (ip + x);
386                 unsigned char c;
387                 c = (*p & R5G6B5_MASK_R) >> R5G6B5_SHIFT_R;
388                 *op++ = (c << 3) | (c >> 2);
389                 c = (*p & R5G6B5_MASK_G) >> R5G6B5_SHIFT_G;
390                 *op++ = (c << 2) | (c >> 4);
391                 c = (*p & R5G6B5_MASK_B) >> R5G6B5_SHIFT_B;
392                 *op++ = (c << 3) | (c >> 2);
393             }
394         }
395         else if (frame_format == FS_EMU_VIDEO_FORMAT_R5G5B5A1) {
396             for (int x = 0; x < row_len; x += frame_bpp) {
397                 unsigned short *p = (unsigned short *) (ip + x);
398                 unsigned char c;
399                 c = (*p & R5G5B5A1_MASK_R) >> R5G5B5A1_SHIFT_R;
400                 *op++ = (c << 3) | (c >> 2);
401                 c = (*p & R5G5B5A1_MASK_G) >> R5G5B5A1_SHIFT_G;
402                 *op++ = (c << 3) | (c >> 2);
403                 c = (*p & R5G5B5A1_MASK_B) >> R5G5B5A1_SHIFT_B;
404                 *op++ = (c << 3) | (c >> 2);
405             }
406         }
407     }
408 
409     int result = fs_image_save_data(path, out_data, cw, ch, 3);
410     if (result) {
411         fs_log("saved screenshot\n");
412     }
413     else {
414         fs_log("error saving screenshot\n");
415     }
416     free(out_data);
417 #if 0
418     free(name);
419     free(path);
420 #endif
421 }
422 
update_texture(void)423 static int update_texture(void)
424 {
425     fs_emu_video_buffer *buffer = fs_emu_video_buffer_lock();
426 #ifdef DEBUG_VIDEO_SYNC
427     printf("u %p\n", buffer);
428 #endif
429     // unlocked in fs_emu_video_after_update
430 
431     if (buffer->seq == 0) {
432         // we haven't received a video frame from the emulator yet
433         return -1;
434     }
435 
436     uint8_t *frame = buffer->data;
437     if (frame == NULL) {
438         return -1;
439     }
440     int is_new_frame = 1;
441     static int last_seq_no = -1;
442     if (buffer->seq == last_seq_no + 1) {
443         // normal
444     }
445     else if (buffer->seq == last_seq_no) {
446         //fs_log("WARNING: repeated frame %d\n", info->seq_no);
447         g_fs_emu_repeated_frames++;
448         if (g_fs_emu_repeated_frames > 9999) {
449             g_fs_emu_repeated_frames = 9999;
450         }
451         g_fs_emu_repeated_frame_time = fs_get_monotonic_time();
452         is_new_frame = 0;
453     } else {
454         int lost_frame_count = buffer->seq - last_seq_no - 1;
455         g_fs_emu_lost_frames += lost_frame_count;
456         g_fs_emu_lost_frame_time = fs_get_monotonic_time();
457         //fs_log("lost %d frame(s)\n", lost_frame_count);
458     }
459     last_seq_no = buffer->seq;
460 
461     is_new_frame = 1;
462 
463     int width = buffer->width;
464     int height = buffer->height;
465     int bpp = buffer->bpp;
466 
467     // check for cropping before screenshot, so we can also save cropped
468     // screenshot
469 
470     if (g_fs_emu_video_crop_mode) {
471         g_crop = buffer->crop;
472         if (g_crop.w == 0) {
473             g_crop.w = width;
474         }
475         if (g_crop.h == 0) {
476             g_crop.h = height;
477         }
478     } else {
479         g_crop.x = 0;
480         g_crop.y = 0;
481         g_crop.w = width;
482         g_crop.h = height;
483     }
484 
485     // g_fs_emu_screenshot is set to 1 when screenshot command is executed
486     // (keyboard shortcut), but screenshot is saved here, as soon as possible.
487 
488     if (g_fs_emu_screenshot > 0) {
489         static char* screenshots_dir = NULL;
490         static char* screenshots_prefix = NULL;
491         static int screenshots_mask = 7;
492         if (screenshots_dir == NULL) {
493             char *path = fs_config_get_string(OPTION_SCREENSHOTS_OUTPUT_DIR);
494             if (path) {
495                 path = fs_emu_path_expand_and_free(path);
496                 if (fs_path_exists(path)) {
497                     screenshots_dir = path;
498                 }
499                 else {
500                     fs_emu_warning("Directory does not exist: %s", path);
501                     g_free(path);
502                 }
503             }
504             if (!screenshots_dir) {
505                 screenshots_dir = g_strdup(fs_get_desktop_dir());
506             }
507             screenshots_mask = fs_config_get_int("screenshots_output_mask");
508             if (screenshots_mask == FS_CONFIG_NONE) {
509                 screenshots_mask = 7;
510             }
511 
512             screenshots_prefix = fs_config_get_string(
513                     "screenshots_output_prefix");
514             if (!screenshots_prefix) {
515                 screenshots_prefix = g_strdup("fs-uae");
516             }
517         }
518 
519         static int total_count = 0;
520         if (g_fs_emu_screenshot == 1) {
521 
522             char *name, *path;
523             time_t t = time(NULL);
524             struct tm tm_struct;
525             struct tm *tm_p = &tm_struct;
526             fs_localtime_r(&t, tm_p);
527 
528             static int count = 0;
529             static char laststrbuf[20] = {};
530 
531             char strbuf[20];
532             strftime(strbuf, 20, "%y%m%d%H%M", tm_p);
533             if (strcmp(strbuf, laststrbuf) != 0) {
534                 count = 0;
535                 memcpy(laststrbuf, strbuf, 20);
536             }
537 
538             count += 1;
539             total_count += 1;
540 
541             if (screenshots_mask & 1) {
542                 name = g_strdup_printf("%s-%s-%s-%02d.png",
543                         screenshots_prefix, "full", strbuf, count);
544                 path = g_build_filename(screenshots_dir, name, NULL);
545                 save_screenshot(path, 0, 0, width, height, count, frame,
546                         width, height, bpp);
547                 g_free(path);
548                 g_free(name);
549             }
550             if (screenshots_mask & 2) {
551                 name = g_strdup_printf("%s-%s-%s-%02d.png",
552                         screenshots_prefix, "crop", strbuf, count);
553                 path = g_build_filename(screenshots_dir, name, NULL);
554                 save_screenshot(path, g_crop.x, g_crop.y, g_crop.w, g_crop.h,
555                         count, frame, width, height, bpp);
556                 g_free(path);
557                 g_free(name);
558             }
559             if (screenshots_mask & 4) {
560                 name = g_strdup_printf("%s-%s-%s-%02d.png",
561                         screenshots_prefix, "real", strbuf, count);
562                 path = g_build_filename(screenshots_dir, name, NULL);
563                 fs_ml_video_screenshot(path);
564                 g_free(path);
565                 g_free(name);
566             }
567             g_fs_emu_screenshot++;
568         }
569         else if (g_fs_emu_screenshot > 4) {
570             // we wait a bit until printing the notification, so the OpenGL
571             // screenshot can be captured before the notification is shown
572             g_fs_emu_screenshot = 0;
573             fse_notify(81830444,
574                     _("Saved screenshot %d"), total_count);
575         }
576         else {
577             g_fs_emu_screenshot++;
578         }
579     }
580 
581     int upload_x, upload_y, upload_w, upload_h;
582     g_effective_viewport_mode = g_viewport_mode;
583     if (buffer->flags & FS_EMU_FORCE_VIEWPORT_CROP_FLAG) {
584         g_effective_viewport_mode = FS_EMU_VIEWPORT_MODE_CROP;
585     }
586 
587     if (g_effective_viewport_mode == FS_EMU_VIEWPORT_MODE_CROP) {
588         fix_border(buffer, &upload_x, &upload_y, &upload_w, &upload_h);
589     } else {
590         upload_x = 0;
591         upload_y = 0;
592         upload_w = buffer->width;
593         upload_h = buffer->height;
594     }
595 
596     int filter = 0;
597     if (0) {
598         filter = 1;
599     } else if (g_fs_emu_scanlines &&
600             (buffer->flags & FS_EMU_NO_SCANLINES_FLAG) == 0) {
601         filter = 2;
602     }
603 
604     if (filter) {
605         if (is_new_frame) {
606             if (g_scanline_buffer_width != buffer->width ||
607                     g_scanline_buffer_height != buffer->height) {
608                 if (g_scanline_buffer) {
609                     free(g_scanline_buffer);
610                 }
611                 g_scanline_buffer = malloc(buffer->width * buffer->height * bpp);
612                 g_scanline_buffer_width = buffer->width;
613                 g_scanline_buffer_height = buffer->height;
614             }
615             if (filter == 2) {
616                 fs_emu_scanline_filter(g_scanline_buffer, buffer,
617                         upload_x, upload_y, upload_w, upload_h,
618                         g_fs_emu_scanlines_dark, g_fs_emu_scanlines_light);
619             } else {
620                 fs_emu_2xcolor_filter(g_scanline_buffer, buffer,
621                         upload_x, upload_y, upload_w, upload_h,
622                         g_fs_emu_scanlines_dark, g_fs_emu_scanlines_light);
623             }
624         }
625         if (g_scanline_buffer) {
626             frame = g_scanline_buffer;
627         }
628     }
629 
630     //fs_log("%d %d %d %d %d %d %d\n", width, height, bpp,
631     //        g_crop.x, g_crop.y, g_crop.w, g_crop.h);
632     g_frame_width = width;
633     g_frame_height = height;
634     g_frame_aspect = buffer->aspect;
635 
636     int gl_buffer_format = 0;
637     int gl_buffer_type = 0;
638     get_buffer_format(&gl_buffer_format, &gl_buffer_type);
639 
640     create_texture_if_needed(width, height);
641     fs_gl_bind_texture(g_frame_texture);
642 
643     uint8_t *gl_buffer_start = frame + ((upload_y * width) + upload_x) * bpp;
644 
645 #ifdef USE_GLES
646     /* we don't have unpack padding in GLES. uploading full width lines instead */
647     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, upload_h,
648             gl_buffer_format, gl_buffer_type, gl_buffer_start);
649 #else
650     fs_gl_unpack_row_length(width);
651     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, upload_w, upload_h,
652             gl_buffer_format, gl_buffer_type, gl_buffer_start);
653 #endif
654     CHECK_GL_ERROR();
655 
656     int update_black_border = 1;
657     if (update_black_border) {
658 
659         int black_left = upload_w;
660         int black_right = g_frame_texture_width;
661         //printf("%d %d\n", black_left, black_right);
662         if (g_frame_texture_black_left < black_right) {
663             black_right = g_frame_texture_black_left;
664             //printf("set g_frame_texture_black_left to %d\n", black_left);
665         }
666         //printf("%d %d\n", black_left, black_right);
667         int black_width = black_right - black_left;
668         if (black_width > 0) {
669             void *black_data = g_malloc0(
670                     black_width * g_frame_texture_height * bpp);
671             //memset(black_data, 0xff, black_width * g_frame_texture_height * bpp);
672             //printf("black1 w %d h %d\n", black_width, g_frame_texture_height);
673             fs_gl_unpack_row_length(0);
674             glTexSubImage2D(GL_TEXTURE_2D, 0, black_left, 0,
675                     black_width, g_frame_texture_height,
676                     gl_buffer_format, gl_buffer_type, black_data);
677             free(black_data);
678             CHECK_GL_ERROR();
679         }
680         g_frame_texture_black_left = black_left;
681 
682         int black_top = upload_h;
683         int black_bottom = g_frame_texture_height;
684         if (g_frame_texture_black_top < black_bottom) {
685             black_bottom = g_frame_texture_black_top;
686         }
687         int black_height = black_bottom - black_top;
688         //printf("---- %d %d\n", black_top, black_bottom);
689         if (black_height > 0) {
690             void *black_data = g_malloc0(
691                     upload_w * black_height * bpp);
692             //memset(black_data, 0xff, upload_w * black_height * bpp);
693             //printf("black2 w %d h %d\n", upload_w, black_height);
694             fs_gl_unpack_row_length(0);
695             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, black_top,
696                     upload_w, black_height,
697                     gl_buffer_format, gl_buffer_type, black_data);
698             free(black_data);
699             CHECK_GL_ERROR();
700         }
701         g_frame_texture_black_top = black_top;
702     }
703     //g_frame_texture_black_left = 100000;
704     //g_frame_texture_black_top = 100000;
705 
706 #if 0
707     int hq_border = 2;
708     if (hq_border >= 1) {
709         /*
710         if (upload_y > 0) {
711             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, upload_y - 1, width, 1,
712                     format, GL_UNSIGNED_BYTE,
713                     frame + (upload_y) * width * bpp);
714             CHECK_GL_ERROR();
715         }
716         if (upload_y + upload_h < g_frame_texture_height) {
717             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, upload_y + upload_h,
718                     width, 1, format, GL_UNSIGNED_BYTE,
719                     frame + (upload_y + upload_h - 1) * width * bpp);
720             CHECK_GL_ERROR();
721         }        */
722 
723         if (upload_h < g_frame_texture_height) {
724             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, upload_h,
725                     width, 1, format, GL_UNSIGNED_BYTE,
726                     frame + (upload_y + upload_h - 1) * width * bpp);
727             CHECK_GL_ERROR();
728         }
729 
730         /*
731         if (upload_x > 0) {
732             glTexSubImage2D(GL_TEXTURE_2D, 0, upload_x - 1, upload_y,
733                     1, upload_h, format, GL_UNSIGNED_BYTE, frame +
734                     ((upload_y) * width + upload_x) * bpp);
735             CHECK_GL_ERROR();
736         }
737         if (upload_x + upload_w < g_frame_texture_width) {
738             glTexSubImage2D(GL_TEXTURE_2D, 0, upload_x + upload_w,
739                     upload_y, 1, upload_h, format,
740                     GL_UNSIGNED_BYTE, frame +
741                     ((upload_y) * width + upload_x + upload_w - 1) * bpp);
742             CHECK_GL_ERROR();
743         }
744         */
745 
746         if (upload_w < g_frame_texture_width) {
747             glTexSubImage2D(GL_TEXTURE_2D, 0, upload_w,
748                     upload_y, 1, upload_h, format,
749                     GL_UNSIGNED_BYTE, frame +
750                     ((upload_y) * width + upload_x + upload_w - 1) * bpp);
751             CHECK_GL_ERROR();
752         }
753     }
754 #if 0
755     if (hq_border >= 2) {
756         if (upload_y + upload_h < g_frame_texture.height - 1) {
757             //printf("1\n");
758             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, upload_y + upload_h + 1,
759                     width, 1, format, GL_UNSIGNED_BYTE,
760                     frame + (upload_y + upload_h - 1) * width * bpp);
761                     //frame + (upload_y + upload_h - 2) * width * bpp);
762         }
763 
764         if (upload_x + upload_w < g_frame_texture.width - 1) {
765             glTexSubImage2D(GL_TEXTURE_2D, 0, upload_x + upload_w + 1,
766                     upload_y, 1, upload_h, format,
767                     GL_UNSIGNED_BYTE, frame +
768                     ((upload_y) * width + upload_x + upload_w - 1) * bpp);
769         }
770     }
771 #endif
772 #endif
773     return last_seq_no;
774 }
775 
render_gloss(double alpha)776 static void render_gloss(double alpha)
777 {
778     fs_gl_blending(1);
779     fs_gl_color4f(alpha, alpha, alpha, alpha);
780     fs_emu_draw_texture_with_size(TEXTURE_GLOSS, -1.0, -1.0, 2.0, 2.0);
781 }
782 
render_quad(float x1,float y1,float x2,float y2,float s1,float t1,float s2,float t2)783 static void render_quad(float x1, float y1, float x2, float y2, float s1,
784                         float t1, float s2, float t2)
785 {
786 #ifdef USE_GLES
787     GLfloat tex[] = {
788         s1, t2,
789         s2, t2,
790         s2, t1,
791         s1, t1
792     };
793     GLfloat vert[] = {
794         x1, y1,
795         x2, y1,
796         x2, y2,
797         x1, y2
798     };
799 
800     glEnableClientState(GL_VERTEX_ARRAY);
801     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
802 
803     glVertexPointer(2, GL_FLOAT, 0, vert);
804     glTexCoordPointer(2, GL_FLOAT, 0, tex);
805     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
806 
807     glDisableClientState(GL_VERTEX_ARRAY);
808     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
809 #else
810     glBegin(GL_QUADS);
811     glTexCoord2d(s1, t2);
812     glVertex2f(x1, y1);
813     glTexCoord2d(s2, t2);
814     glVertex2f(x2, y1);
815     glTexCoord2d(s2, t1);
816     glVertex2f(x2, y2);
817     glTexCoord2d(s1, t1);
818     glVertex2f(x1, y2);
819     glEnd();
820 #endif
821     CHECK_GL_ERROR();
822 }
823 
render_overlays(void)824 static void render_overlays(void)
825 {
826     fs_mutex_lock(g_overlay_mutex);
827     for (int i = 0; i < FS_EMU_MAX_OVERLAYS; i++) {
828         int state = g_overlay_status[i].state;
829 #if 0
830         if (g_overlay_status[i].render_status &&
831                 g_overlay_status[i].was_disabled) {
832             show = 0;
833         }
834         else if (!g_overlay_status[i].render_status &&
835                 g_overlay_status[i].was_enabled) {
836             show = 1;
837         }
838         g_overlay_status[i].was_enabled = 0;
839         g_overlay_status[i].was_disabled = 0;
840 #endif
841         g_overlay_status[i].render_state = state;
842         if (state) {
843             g_overlay_status[i].render_status++;
844         }
845         else {
846             g_overlay_status[i].render_status = 0;
847         }
848     }
849     fs_mutex_unlock(g_overlay_mutex);
850 
851     for (int i = 0; i < FS_EMU_MAX_OVERLAYS; i++) {
852         //if (g_overlay_status[i].render_status < 1) {
853         //    continue;
854         //}
855         int state = g_overlay_status[i].render_state;
856         if (state < 0) state = 0;
857         if (state > FS_EMU_MAX_OVERLAY_STATES - 1) {
858             state = FS_EMU_MAX_OVERLAY_STATES - 1;
859         }
860 
861         fs_emu_texture *overlay_texture = \
862                 g_fs_emu_theme.overlays[i].textures[state];
863         if (!overlay_texture) {
864             continue;
865         }
866 
867         float w = g_fs_emu_theme.overlays[i].w;
868         float h = g_fs_emu_theme.overlays[i].h;
869         float x1 = g_fs_emu_theme.overlays[i].x;
870         if (g_fs_emu_theme.overlays[i].anchor & FS_EMU_ANCHOR_RIGHT_BIT) {
871             x1 = x1 + 1.0 - w;
872         }
873         float y1 = g_fs_emu_theme.overlays[i].y;
874         if (g_fs_emu_theme.overlays[i].anchor & FS_EMU_ANCHOR_BOTTOM_BIT) {
875             y1 = y1 + 1.0 - h;
876         }
877 
878         float x2 = x1 + w;
879         float y2 = y1 + h;
880 
881         x1 = -1.0 + 2.0 * x1;
882         x2 = -1.0 + 2.0 * x2;
883         y1 =  1.0 - 2.0 * y1;
884         y2 =  1.0 - 2.0 * y2;
885 
886         fs_gl_blending(1);
887         fs_gl_texturing(1);
888         fs_gl_color4f(1.0, 1.0, 1.0, 1.0);
889         fs_emu_set_texture(overlay_texture);
890 
891 #ifdef USE_GLES
892         GLfloat tex[] = {
893             0.0, 0.0,
894             1.0, 0.0,
895             1.0, 1.0,
896             0.0, 1.0
897         };
898         GLfloat vert[] = {
899             x1, y1,
900             x2, y1,
901             x2, y2,
902             x1, y2
903         };
904 
905         glEnableClientState(GL_VERTEX_ARRAY);
906         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
907 
908         glVertexPointer(2, GL_FLOAT, 0, vert);
909         glTexCoordPointer(2, GL_FLOAT, 0, tex);
910         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
911 
912         glDisableClientState(GL_VERTEX_ARRAY);
913         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
914 #else
915         glBegin(GL_QUADS);
916         glTexCoord2f(0.0, 0.0); glVertex2f(x1, y1);
917         glTexCoord2f(1.0, 0.0); glVertex2f(x2, y1);
918         glTexCoord2f(1.0, 1.0); glVertex2f(x2, y2);
919         glTexCoord2f(0.0, 1.0); glVertex2f(x1, y2);
920         glEnd();
921 #endif
922     }
923 }
924 
925 #include <fs/emu/hacks.h>
926 
927 double fs_emu_video_scale_x = 0.0;
928 double fs_emu_video_scale_y = 0.0;
929 double fs_emu_video_offset_x = 1.0;
930 double fs_emu_video_offset_y = 1.0;
931 
render_frame(double alpha,int perspective)932 static void render_frame(double alpha, int perspective)
933 {
934 #ifdef WITH_LUA
935     fs_emu_lua_run_handler("on_fs_emu_render_frame");
936 #endif
937 
938 #ifdef WITH_LUA
939 
940 #if 0
941     fs_emu_acquire_lua();
942     lua_getglobal(fs_emu_lua_state, "on_fs_emu_render_frame");
943     if (lua_isnil(fs_emu_lua_state, -1)) {
944         lua_pop(fs_emu_lua_state, 1);
945     }
946     else if (lua_pcall(fs_emu_lua_state, 0, 0, 0) != 0) {
947         fs_emu_lua_log_error("error calling on_render_frame");
948         lua_pop(fs_emu_lua_state, 1);
949     }
950     //else if (!lua_isnumber(fs_emu_lua_state, -1)) {
951     //    fs_log("fs_emu_on_render_frame must return a number\n");
952     //}
953     else {
954     //    //int handled = lua_tonumber(fs_emu_lua_state, -1);
955     //    //lua_pop(fs_emu_lua_state, 1);
956     //    //if (handled) {
957     //    //    return;
958     //    //}
959         //fs_emu_release_lua();
960         //return;
961     }
962     fs_emu_release_lua();
963 #endif
964 
965 #endif
966 
967     int fox = g_fs_emu_theme.display_x;
968     int foy = g_fs_emu_theme.display_y;
969     int fow = g_fs_emu_theme.display_w;
970     int foh = g_fs_emu_theme.display_h;
971     int frame_override = fow != g_fs_emu_theme.width ||
972             foh != g_fs_emu_theme.height;
973     if (g_frame_override) {
974         fox = g_frame_override_x;
975         foy = g_frame_override_y;
976         fow = g_frame_override_w;
977         foh = g_frame_override_h;
978         frame_override = 1;
979     }
980 
981     if (frame_override) {
982         float color = 1.0;
983         if (g_frame_texture == 0) {
984             // texture has not been created yet
985             color = 0.0;
986             fs_gl_texturing(0);
987         }
988         else {
989             fs_gl_texturing(1);
990             fs_gl_bind_texture(g_frame_texture);
991         }
992         fs_gl_blending(0);
993         fs_gl_color4f(color, color, color, 1.0);
994 
995         float x1 = -1.0 + fox * 2.0 / g_l_scale_x;
996         float x2 = -1.0 + (fox + fow) * 2.0 / g_l_scale_x;
997         float y2 = 1.0 - foy * 2.0 / g_l_scale_y;
998         float y1 = 1.0 - (foy + foh) * 2.0 / g_l_scale_y;
999 
1000         double s1 = 0.0;
1001         double t1 = 0.0;
1002         double s2 = (double) g_crop.w / g_frame_texture_width;
1003         double t2 = (double) g_crop.h / g_frame_texture_height;
1004 
1005         int shader_result = 0;
1006 #ifdef WITH_XML_SHADER
1007         if (g_frame_texture) {
1008             // only try to render with shader passes if we have a valid texture
1009 
1010             int screen_w = fs_ml_video_width();
1011             int screen_h = fs_ml_video_height();
1012             double doutput_w = screen_w;
1013             double doutput_h = screen_h;
1014             doutput_w = doutput_w * g_frame_override_w / (double) g_l_scale_x;
1015             doutput_h = doutput_h * g_frame_override_h / (double) g_l_scale_y;
1016             int output_w = doutput_w + 0.5;
1017             int output_h = doutput_h + 0.5;
1018 
1019             shader_result = fs_emu_xml_shader_render(g_frame_texture,
1020                     g_frame_texture_width, g_frame_texture_height,
1021                     g_crop.w, g_crop.h, output_w, output_h,
1022                     x1, y1, x2, y2, 0, 1.0);
1023         }
1024 #endif
1025         if (!shader_result) {
1026             render_quad(x1, y1, x2, y2, s1, t1, s2, t2);
1027         }
1028 
1029         render_overlays();
1030         return;
1031     }
1032 
1033     int input_w = g_crop.w;
1034     int input_h = g_crop.h;
1035 
1036     if (fse_scale_mode() == FSE_SCALE_LEGACY) {
1037         if (fs_emu_video_get_aspect_correction()) {
1038             /* The old "aspect correction" was just square pixels. */
1039             fse_set_stretch_mode(FSE_STRETCH_NONE);
1040         } else {
1041             fse_set_stretch_mode(FSE_STRETCH_FILL_SCREEN);
1042         }
1043     }
1044 
1045     int screen_w = fs_ml_video_width();
1046     int screen_h = fs_ml_video_height();
1047     int output_w;
1048     int output_h;
1049     int offset_x;
1050     int offset_y;
1051 
1052     fse_set_render_size(screen_w, screen_h);
1053     fse_calculate_video_rectangle(
1054                 screen_w, screen_h, g_crop.w, g_crop.h,
1055                 &offset_x, &offset_y, &output_w, &output_h, NULL, NULL);
1056     fse_set_view_rectangle(offset_x, offset_y, output_w, output_h);
1057 
1058 #if 0
1059     double emu_aspect = (double) input_w / (double) input_h;
1060     emu_aspect *= g_frame_aspect;
1061 
1062     double screen_aspect = (double) screen_w / (double) screen_h;
1063 
1064     double doutput_w = screen_w;
1065     double doutput_h = screen_h;
1066 
1067     if (fs_emu_video_get_aspect_correction()) {
1068         if (emu_aspect > screen_aspect) {
1069             // emu video is wider than screen, typical for 4:3 or 5:4 monitors
1070             double h = screen_aspect / emu_aspect;
1071             doutput_h *= h;
1072         } else {
1073             // typical scenario for wide-screen monitors
1074             double w = emu_aspect / screen_aspect;
1075             doutput_w *= w;
1076         }
1077     }
1078 
1079     if (g_scale_x < 0.0) {
1080         doutput_w *= -g_scale_x;
1081     } else {
1082         doutput_w = input_w * g_scale_x;
1083     }
1084     if (g_scale_y < 0.0) {
1085         doutput_h *= -g_scale_y;
1086     } else {
1087         doutput_h = input_h * g_scale_y;
1088     }
1089 
1090     // round to nearest integer
1091     int output_w = doutput_w + 0.5;
1092     int output_h = doutput_h + 0.5;
1093     //printf("output size: %d %d\n", output_w, output_h);
1094 
1095     int offset_x = (screen_w - output_w) * g_align_x;
1096     int offset_y = (screen_h - output_h) * (1.0 - g_align_y);
1097 
1098     //printf("w %d h %d x %d y %d\n", output_w, output_h, offset_x, offset_y);
1099 #endif
1100 
1101     fs_emu_video_scale_x = (double) input_w / output_w;
1102     fs_emu_video_scale_y = (double) input_h / output_h;
1103 
1104     fs_emu_video_offset_x = offset_x - g_crop.x / fs_emu_video_scale_x;
1105     fs_emu_video_offset_y = offset_y - g_crop.y / fs_emu_video_scale_y;
1106 
1107     double s1 = 0.0;
1108     double t1 = 0.0;
1109     double s2 = (double) g_crop.w / g_frame_texture_width;
1110     double t2 = (double) g_crop.h / g_frame_texture_height;
1111 
1112     double x1 = -1.0 + 2.0 * (double) offset_x / screen_w;
1113     double y1 = -1.0 + 2.0 * (double) offset_y / screen_h;
1114     double x2 = -1.0 + 2.0 * (double) (offset_x + output_w) / screen_w;
1115     double y2 = -1.0 + 2.0 * (double) (offset_y + output_h) / screen_h;
1116 
1117 #if 0
1118     double emu_aspect;
1119     if (g_fs_emu_video_crop_mode) {
1120         //if (g_viewport_mode == FS_EMU_VIEWPORT_MODE_CROP) {
1121         /*
1122         s1 = (double) g_crop.x / g_frame_texture_width;
1123         s2 = (double) (g_crop.x + g_crop.w) / g_frame_texture_width;
1124         t1 = (double) g_crop.y / g_frame_texture_height;
1125         t2 = (double) (g_crop.y + g_crop.h) / g_frame_texture_height;
1126         */
1127 
1128         s1 = 0.0;
1129         s2 = (double) (g_crop.w) / g_frame_texture_width;
1130         t1 = 0.0;
1131         t2 = (double) (g_crop.h) / g_frame_texture_height;
1132 
1133         emu_aspect = (double) g_crop.w / (double) g_crop.h;
1134     }
1135     else {
1136         s1 = 0.0;
1137         s2 = (double) g_frame_width / g_frame_texture_width;
1138         t1 = 0.0;
1139         t2 = (double)  g_frame_height / g_frame_texture_height;
1140         emu_aspect = (double) g_frame_width / (double) g_frame_height;
1141     }
1142 
1143     emu_aspect *= g_frame_aspect;
1144 
1145     double x1 = -1.0;
1146     double x2 =  1.0;
1147     double y1 = -1.0;
1148     double y2 =  1.0;
1149 
1150 #endif
1151 
1152     double repeat_right_border = 0;
1153     //int repeat_bottom_border = 0;
1154 
1155 #if 0
1156     if (fs_emu_video_get_aspect_correction()) {
1157         double screen_aspect = (double) fs_ml_video_width() /
1158                 (double) fs_ml_video_height();
1159         if (emu_aspect > screen_aspect) {
1160             // emu video is wider than screen
1161             double h = screen_aspect / emu_aspect;
1162             //double padding = (2.0 - 2.0 * h) / 2.0;
1163             double padding = 1.0 - h;
1164             if (g_effective_viewport_mode == FS_EMU_VIEWPORT_MODE_CROP) {
1165                 y1 += padding;
1166                 y2 -= padding;
1167             }
1168             else { // FS_EMU_VIEWPORT_MODE_CENTER
1169                 t1 -= padding / 2.0;
1170                 t2 += padding / 2.0;
1171                 //y2 -= padding;
1172             }
1173         }
1174         else {
1175             double w = emu_aspect / screen_aspect;
1176             //double padding = (2.0 - 2.0 * w) / 2.0;
1177             double padding = 1.0 - w;
1178             if (g_effective_viewport_mode == FS_EMU_VIEWPORT_MODE_CROP) {
1179                 x1 += padding;
1180                 x2 -= padding;
1181             }
1182             else { // FS_EMU_VIEWPORT_MODE_CENTER
1183                 //s1 -= padding / 4.0;
1184 
1185                 // FIXME: THIS IS WRONG
1186                 s1 -= padding / 2.0;
1187                 //s2 += padding / 2.0;
1188                 x2 -= padding;
1189                 //repeat_right_border = x2;
1190                 repeat_right_border = 1.0 - padding;
1191             }
1192         }
1193     }
1194 #endif
1195 
1196     // if video is not stretched, we render black rectangles to cover
1197     // the rest of the screen
1198     if (x1 > -1.0 || x2 < 1.0 || y1 > -1.0 || y2 < 1.0) {
1199         fs_gl_texturing(0);
1200         if (alpha < 1.0) {
1201             fs_gl_blending(1);
1202             fs_gl_color4f(0.0, 0.0, 0.0, alpha);
1203         }
1204         else {
1205             fs_gl_blending(0);
1206             fs_gl_color4f(0.0, 0.0, 0.0, 1.0);
1207         }
1208 
1209         if (x1 > -1.0) {
1210 #ifdef USE_GLES
1211             GLfloat vert[] = {
1212                    -1.0, -1.0,
1213                    x1, -1.0,
1214                    x1,  1.0,
1215                    -1.0, 1.0
1216             };
1217             glEnableClientState(GL_VERTEX_ARRAY);
1218             glVertexPointer(2, GL_FLOAT, 0, vert);
1219             glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1220             glDisableClientState(GL_VERTEX_ARRAY);
1221 #else
1222             glBegin(GL_QUADS);
1223             glVertex2f(-1.0, -1.0);
1224             glVertex2f(  x1, -1.0);
1225             glVertex2f(  x1,  1.0);
1226             glVertex2f(-1.0,  1.0);
1227             glEnd();
1228 #endif
1229             CHECK_GL_ERROR();
1230         }
1231         if (x2 < 1.0) {
1232 #ifdef USE_GLES
1233             GLfloat vert[] = {
1234                     x2, -1.0,
1235                    1.0, -1.0,
1236                    1.0,  1.0,
1237                     x2,  1.0
1238             };
1239             glEnableClientState(GL_VERTEX_ARRAY);
1240             glVertexPointer(2, GL_FLOAT, 0, vert);
1241             glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1242             glDisableClientState(GL_VERTEX_ARRAY);
1243 #else
1244             glBegin(GL_QUADS);
1245             glVertex2f(  x2, -1.0);
1246             glVertex2f( 1.0, -1.0);
1247             glVertex2f( 1.0,  1.0);
1248             glVertex2f(  x2,  1.0);
1249             glEnd();
1250 #endif
1251             CHECK_GL_ERROR();
1252         }
1253         if (y1 > -1.0) {
1254 #ifdef USE_GLES
1255             GLfloat vert[] = {
1256                    x1, -1.0,
1257                    x2, -1.0,
1258                    x2,   y1,
1259                    x1,   y1
1260             };
1261             glEnableClientState(GL_VERTEX_ARRAY);
1262             glVertexPointer(2, GL_FLOAT, 0, vert);
1263             glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1264             glDisableClientState(GL_VERTEX_ARRAY);
1265 #else
1266             glBegin(GL_QUADS);
1267             glVertex2f(x1, -1.0);
1268             glVertex2f(x2, -1.0);
1269             glVertex2f(x2,   y1);
1270             glVertex2f(x1,   y1);
1271             glEnd();
1272 #endif
1273             // left side (3D)
1274 #ifdef USE_GLES
1275             GLfloat vert2[] = {
1276                    -1.0, -1.0, -0.1,
1277                    -1.0, -1.0,  0.0,
1278                    -1.0,   y1,  0.0,
1279                    -1.0,   y1, -0.1
1280             };
1281             glEnableClientState(GL_VERTEX_ARRAY);
1282             glVertexPointer(3, GL_FLOAT, 0, vert2);
1283             glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1284             glDisableClientState(GL_VERTEX_ARRAY);
1285 #else
1286             glBegin(GL_QUADS);
1287             glVertex3f(-1.0, -1.0, -0.1);
1288             glVertex3f(-1.0, -1.0,  0.0);
1289             glVertex3f(-1.0,   y1,  0.0);
1290             glVertex3f(-1.0,   y1, -0.1);
1291             glEnd();
1292 #endif
1293             CHECK_GL_ERROR();
1294         }
1295         if (y2 < 1.0) {
1296 #ifdef USE_GLES
1297             GLfloat vert[] = {
1298                 x1, y2,
1299                 x2, y2,
1300                 x2, 1.0,
1301                 x1, 1.0
1302             };
1303             glEnableClientState(GL_VERTEX_ARRAY);
1304             glVertexPointer(2, GL_FLOAT, 0, vert);
1305             glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1306             glDisableClientState(GL_VERTEX_ARRAY);
1307 #else
1308             glBegin(GL_QUADS);
1309             glVertex2f(x1,   y2);
1310             glVertex2f(x2,   y2);
1311             glVertex2f(x2,  1.0);
1312             glVertex2f(x1,  1.0);
1313             glEnd();
1314 #endif
1315             // left side (3D)
1316 #ifdef USE_GLES
1317             GLfloat vert2[] = {
1318                    -1.0,   y2, -0.1,
1319                    -1.0,   y2,  0.0,
1320                    -1.0,  1.0,  0.0,
1321                    -1.0,  1.0, -0.1
1322             };
1323             glEnableClientState(GL_VERTEX_ARRAY);
1324             glVertexPointer(3, GL_FLOAT, 0, vert2);
1325             glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1326             glDisableClientState(GL_VERTEX_ARRAY);
1327 #else
1328             glBegin(GL_QUADS);
1329             glVertex3f(-1.0,  y2, -0.1);
1330             glVertex3f(-1.0,  y2,  0.0);
1331             glVertex3f(-1.0, 1.0,  0.0);
1332             glVertex3f(-1.0, 1.0, -0.1);
1333             glEnd();
1334 #endif
1335             CHECK_GL_ERROR();
1336         }
1337     }
1338     //printf("--- render frame done ---\n");
1339 
1340     int render_textured_side = 0;
1341     if (perspective) {
1342         // render left side in 3d mode
1343         fs_gl_blending(0);
1344         if (x1 > -1.0) {
1345             // emu screen does not reach screen edge - side will be black
1346             fs_gl_texturing(0);
1347             fs_gl_color4f(0.0, 0.0, 0.0, alpha);
1348         }
1349         /*
1350         else if (fs_emu_using_shader()) {
1351             // for simplicity, render black side when using shaders (the
1352             // problem is that shaders can render directly to the framebuffer,
1353             // and not necessarily to a texture. This can be fixed, but
1354             // side rendering should be copied to fs_emu_render_with_shader
1355             // in this case, so the side can be rendered with the shader
1356             // too...
1357             //fs_gl_texturing(0);
1358             //fs_gl_color4f(0.0, 0.0, 0.0, alpha);
1359         }
1360         */
1361         else {
1362             render_textured_side = 1;
1363             fs_gl_texturing(1);
1364             fs_gl_bind_texture(g_frame_texture);
1365             fs_gl_color4f(0.33 * alpha, 0.33 * alpha, 0.33 * alpha, alpha);
1366         }
1367 
1368         if (render_textured_side == 0
1369 #ifdef WITH_XML_SHADER
1370                 || !fs_emu_xml_shader_is_enabled()
1371 #endif
1372         ) {
1373 #ifdef USE_GLES
1374             GLfloat tex[] = {
1375                 s1, t2,
1376                 s1, t2,
1377                 s1, t1,
1378                 s1, t1
1379             };
1380             GLfloat vert[] = {
1381                 -1.0, y1, -0.1,
1382                 -1.0, y1, 0.0,
1383                 -1.0, y2, 0.0,
1384                 -1.0, y2, -0.1
1385             };
1386 
1387             glEnableClientState(GL_VERTEX_ARRAY);
1388             glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1389 
1390             glVertexPointer(3, GL_FLOAT, 0, vert);
1391             glTexCoordPointer(2, GL_FLOAT, 0, tex);
1392             glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1393 
1394             glDisableClientState(GL_VERTEX_ARRAY);
1395             glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1396 #else
1397             glBegin(GL_QUADS);
1398             glTexCoord2d(s1, t2); glVertex3f(-1.0, y1, -0.1);
1399             glTexCoord2d(s1, t2); glVertex3f(-1.0, y1,  0.0);
1400             glTexCoord2d(s1, t1); glVertex3f(-1.0, y2,  0.0);
1401             glTexCoord2d(s1, t1); glVertex3f(-1.0, y2, -0.1);
1402             glEnd();
1403 #endif
1404             CHECK_GL_ERROR();
1405         }
1406     }
1407 
1408     float color = 1.0;
1409     if (g_frame_texture == 0) {
1410         // texture has not been created yet
1411         color = 0.0;
1412         fs_gl_texturing(0);
1413     } else {
1414         fs_gl_texturing(1);
1415         fs_gl_bind_texture(g_frame_texture);
1416     }
1417     if (alpha < 1.0) {
1418         fs_gl_blending(1);
1419         fs_gl_color4f(color * alpha, color * alpha, color * alpha, alpha);
1420     } else {
1421         fs_gl_blending(0);
1422         fs_gl_color4f(color, color, color, 1.0);
1423     }
1424 
1425     int shader_result = 0;
1426 #ifdef WITH_XML_SHADER
1427     if (g_frame_texture) {
1428         // only try to render with shader passes if we have a valid texture
1429         shader_result = fs_emu_xml_shader_render(g_frame_texture,
1430                 g_frame_texture_width, g_frame_texture_height,
1431                 g_crop.w, g_crop.h, output_w, output_h,
1432                 x1, y1, x2, y2, render_textured_side, alpha);
1433     }
1434 #endif
1435     if (!shader_result) {
1436         render_quad(x1, y1, x2, y2, s1, t1, s2, t2);
1437     }
1438 
1439     //repeat_right_border = 0;
1440     if (repeat_right_border > 0.0) {
1441         s1 = s2 = (double) (g_frame_width - 1) / g_frame_texture_width;
1442 
1443 #ifdef USE_GLES
1444         GLfloat tex[] = {
1445             s1, t2,
1446             s2, t2,
1447             s2, t1,
1448             s1, t1
1449         };
1450         GLfloat vert[] = {
1451             repeat_right_border, y1,
1452             1.0, y1,
1453             1.0, y2,
1454             repeat_right_border, y2
1455         };
1456 
1457         glEnableClientState(GL_VERTEX_ARRAY);
1458         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1459 
1460         glVertexPointer(2, GL_FLOAT, 0, vert);
1461         glTexCoordPointer(2, GL_FLOAT, 0, tex);
1462         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1463 
1464         glDisableClientState(GL_VERTEX_ARRAY);
1465         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1466 #else
1467         glBegin(GL_QUADS);
1468         glTexCoord2d(s1, t2); glVertex2f(repeat_right_border, y1);
1469         glTexCoord2d(s2, t2); glVertex2f(1.0, y1);
1470         glTexCoord2d(s2, t1); glVertex2f(1.0, y2);
1471         glTexCoord2d(s1, t1); glVertex2f(repeat_right_border, y2);
1472         glEnd();
1473 #endif
1474         CHECK_GL_ERROR();
1475 
1476         // so the following code does not render black rectangle over
1477         // the right border
1478         x2 = 1.0;
1479     }
1480 
1481     if (g_fs_emu_overlay_texture) {
1482         fs_gl_blending(1);
1483         fs_gl_texturing(1);
1484         fs_gl_color4f(alpha, alpha, alpha, alpha);
1485         //fs_gl_bind_texture(g_fs_emu_overlay_texture->texture);
1486         fs_emu_set_texture(g_fs_emu_overlay_texture);
1487 
1488 #ifdef USE_GLES
1489         GLfloat tex[] = {
1490             0.0, 1.0,
1491             1.0, 1.0,
1492             1.0, 0.0,
1493             0.0, 0.0
1494         };
1495         GLfloat vert[] = {
1496             -1.0, -1.0,
1497              1.0, -1.0,
1498              1.0, 1.0,
1499             -1.0, 1.0
1500         };
1501 
1502         glEnableClientState(GL_VERTEX_ARRAY);
1503         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1504 
1505         glVertexPointer(2, GL_FLOAT, 0, vert);
1506         glTexCoordPointer(2, GL_FLOAT, 0, tex);
1507         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1508 
1509         glDisableClientState(GL_VERTEX_ARRAY);
1510         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1511 #else
1512         glBegin(GL_QUADS);
1513         glTexCoord2f(0.0, 1.0); glVertex2f(-1.0, -1.0);
1514         glTexCoord2f(1.0, 1.0); glVertex2f( 1.0, -1.0);
1515         glTexCoord2f(1.0, 0.0); glVertex2f( 1.0,  1.0);
1516         glTexCoord2f(0.0, 0.0); glVertex2f(-1.0,  1.0);
1517         glEnd();
1518 #endif
1519     }
1520     fs_gl_color4f(1.0, 1.0, 1.0, 1.0);
1521     fse_render_frame();
1522     render_overlays();
1523 }
1524 
render_glow(double opacity)1525 static void render_glow(double opacity)
1526 {
1527     //printf("--- render glow ---\n");
1528     float tx1, ty1, tx2, ty2;
1529     //const double dx = 0.1 * 9.0 / 16.0;
1530     const double dx = 0.15;
1531     const double dy = 0.15 * 16.0 / 9.0;
1532     const double z = 0.0;
1533     const double s = 0.65 * opacity;
1534     fs_gl_color4f(s, s, s, s);
1535     fs_gl_blending(1);
1536     //fs_emu_set_texture(g_tex_glow_top);
1537     fs_emu_prepare_texture(TEXTURE_GLOW_TOP, &tx1, &ty1, &tx2, &ty2);
1538     // render top edge
1539 #ifdef USE_GLES
1540     GLfloat tex[] = {
1541         tx1, ty2,
1542         tx2, ty2,
1543         tx2, ty1,
1544         tx1, ty1
1545     };
1546     GLfloat vert[] = {
1547         -1.0 + dx, 1.0 - dy, z,
1548         1.0 - dx, 1.0 - dy, z,
1549         1.0 - dx, 1.0 + dy, z,
1550         -1.0 + dx, 1.0 + dy, z
1551     };
1552 
1553     glEnableClientState(GL_VERTEX_ARRAY);
1554     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1555 
1556     glVertexPointer(3, GL_FLOAT, 0, vert);
1557     glTexCoordPointer(2, GL_FLOAT, 0, tex);
1558     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1559 
1560     glDisableClientState(GL_VERTEX_ARRAY);
1561     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1562 #else
1563     glBegin(GL_QUADS);
1564     glTexCoord2f(tx1, ty2); glVertex3f(-1.0 + dx, 1.0 - dy, z);
1565     glTexCoord2f(tx2, ty2); glVertex3f( 1.0 - dx, 1.0 - dy, z);
1566     glTexCoord2f(tx2, ty1); glVertex3f( 1.0 - dx, 1.0 + dy, z);
1567     glTexCoord2f(tx1, ty1); glVertex3f(-1.0 + dx, 1.0 + dy, z);
1568     glEnd();
1569 #endif
1570     CHECK_GL_ERROR();
1571     // render corners
1572     fs_emu_prepare_texture(TEXTURE_GLOW_TOP_LEFT, &tx1, &ty1, &tx2, &ty2);
1573 #ifdef USE_GLES
1574     GLfloat tex2[] = {
1575         // top left corner
1576         tx1, ty2,
1577         tx2, ty2,
1578         tx2, ty1,
1579         tx1, ty1,
1580         // top right corner
1581         tx2, ty2,
1582         tx1, ty2,
1583         tx1, ty1,
1584         tx2, ty1
1585     };
1586     GLfloat vert2[] = {
1587         // top left corner
1588         -1.0 - dx, 1.0 - dy, z,
1589         -1.0 + dx, 1.0 - dy, z,
1590         -1.0 + dx, 1.0 + dy, z,
1591         -1.0 - dx, 1.0 + dy, z,
1592         // top right corner
1593         1.0 - dx, 1.0 - dy, z,
1594         1.0 + dx, 1.0 - dy, z,
1595         1.0 + dx, 1.0 + dy, z,
1596         1.0 - dx, 1.0 + dy, z
1597     };
1598 
1599     glEnableClientState(GL_VERTEX_ARRAY);
1600     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1601 
1602     glVertexPointer(3, GL_FLOAT, 0, vert2);
1603     glTexCoordPointer(2, GL_FLOAT, 0, tex2);
1604     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1605     glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
1606 
1607     glDisableClientState(GL_VERTEX_ARRAY);
1608     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1609 #else
1610     glBegin(GL_QUADS);
1611     // top left corner
1612     glTexCoord2f(tx1, ty2); glVertex3f(-1.0 - dx, 1.0 - dy, z);
1613     glTexCoord2f(tx2, ty2); glVertex3f(-1.0 + dx, 1.0 - dy, z);
1614     glTexCoord2f(tx2, ty1); glVertex3f(-1.0 + dx, 1.0 + dy, z);
1615     glTexCoord2f(tx1, ty1); glVertex3f(-1.0 - dx, 1.0 + dy, z);
1616     // top right corner
1617     glTexCoord2f(tx2, ty2); glVertex3f( 1.0 - dx, 1.0 - dy, z);
1618     glTexCoord2f(tx1, ty2); glVertex3f( 1.0 + dx, 1.0 - dy, z);
1619     glTexCoord2f(tx1, ty1); glVertex3f( 1.0 + dx, 1.0 + dy, z);
1620     glTexCoord2f(tx2, ty1); glVertex3f( 1.0 - dx, 1.0 + dy, z);
1621     glEnd();
1622 #endif
1623     CHECK_GL_ERROR();
1624     // render left and right edge
1625     fs_emu_prepare_texture(TEXTURE_GLOW_LEFT, &tx1, &ty1, &tx2, &ty2);
1626     //fs_emu_set_texture(g_tex_glow_left);
1627 #ifdef USE_GLES
1628     GLfloat color3[] = {
1629         // left edge
1630         s, s, s, s,
1631         s, s, s, s,
1632         s, s, s, s,
1633         s, s, s, s,
1634         // right edge
1635         s, s, s, s,
1636         s, s, s, s,
1637         s, s, s, s,
1638         s, s, s, s,
1639         // left edge bottom
1640         0, 0, 0, 0,
1641         0, 0, 0, 0,
1642         s, s, s, s,
1643         s, s, s, s,
1644         // right edge bottom
1645         0, 0, 0, 0,
1646         0, 0, 0, 0,
1647         s, s, s, s,
1648         s, s, s, s,
1649     };
1650     GLfloat tex3[] = {
1651         // left edge
1652         tx1, ty2,
1653         tx2, ty2,
1654         tx2, ty1,
1655         tx1, ty1,
1656         // right edge
1657         tx2, ty2,
1658         tx1, ty2,
1659         tx1, ty1,
1660         tx2, ty1,
1661         // left edge bottom
1662         tx1, ty2,
1663         tx2, ty2,
1664         tx2, ty1,
1665         tx1, ty1,
1666         // right edge bottom
1667         tx2, ty2,
1668         tx1, ty2,
1669         tx1, ty1,
1670         tx2, ty1
1671     };
1672     GLfloat vert3[] = {
1673         // left edge
1674         -1.0 - dx, -0.5, z,
1675         -1.0 + dx, -0.5, z,
1676         -1.0 + dx, 1.0 - dy, z,
1677         -1.0 - dx, 1.0 - dy, z,
1678         // right edge
1679         1.0 - dx, -0.5, z,
1680         1.0 + dx, -0.5, z,
1681         1.0 + dx, 1.0 - dy, z,
1682         1.0 - dx, 1.0 - dy, z,
1683         // left edge bottom
1684         -1.0 - dx, -1.0, z,
1685         -1.0 + dx, -1.0, z,
1686         -1.0 + dx, -0.5, z,
1687         -1.0 - dx, -0.5, z,
1688         // right edge bottom
1689         1.0 - dx, -1.0, z,
1690         1.0 + dx, -1.0, z,
1691         1.0 + dx, -0.5, z,
1692         1.0 - dx, -0.5, z
1693     };
1694 
1695     glEnableClientState(GL_VERTEX_ARRAY);
1696     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1697     glEnableClientState(GL_COLOR_ARRAY);
1698 
1699     glColorPointer(4, GL_FLOAT, 0, color3);
1700     glVertexPointer(3, GL_FLOAT, 0, vert3);
1701     glTexCoordPointer(2, GL_FLOAT, 0, tex3);
1702     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1703     glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
1704     glDrawArrays(GL_TRIANGLE_FAN, 8, 4);
1705     glDrawArrays(GL_TRIANGLE_FAN, 12, 4);
1706 
1707     glDisableClientState(GL_VERTEX_ARRAY);
1708     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1709     glDisableClientState(GL_COLOR_ARRAY);
1710 #else
1711     glBegin(GL_QUADS);
1712     // left edge
1713     glTexCoord2f(tx1, ty2); glVertex3f(-1.0 - dx, -0.5, z);
1714     glTexCoord2f(tx2, ty2); glVertex3f(-1.0 + dx, -0.5, z);
1715     glTexCoord2f(tx2, ty1); glVertex3f(-1.0 + dx, 1.0 - dy, z);
1716     glTexCoord2f(tx1, ty1); glVertex3f(-1.0 - dx, 1.0 - dy, z);
1717     // right edge
1718     glTexCoord2f(tx2, ty2); glVertex3f( 1.0 - dx, -0.5, z);
1719     glTexCoord2f(tx1, ty2); glVertex3f( 1.0 + dx, -0.5, z);
1720     glTexCoord2f(tx1, ty1); glVertex3f( 1.0 + dx, 1.0 - dy, z);
1721     glTexCoord2f(tx2, ty1); glVertex3f( 1.0 - dx, 1.0 - dy, z);
1722     // left edge bottom
1723     fs_gl_color4f(0.0, 0.0, 0.0, 0.0);
1724     glTexCoord2f(tx1, ty2); glVertex3f(-1.0 - dx, -1.0, z);
1725     glTexCoord2f(tx2, ty2); glVertex3f(-1.0 + dx, -1.0, z);
1726     fs_gl_color4f(s, s, s, s);
1727     glTexCoord2f(tx2, ty1); glVertex3f(-1.0 + dx, -0.5, z);
1728     glTexCoord2f(tx1, ty1); glVertex3f(-1.0 - dx, -0.5, z);
1729     // right edge bottom
1730     fs_gl_color4f(0.0, 0.0, 0.0, 0.0);
1731     glTexCoord2f(tx2, ty2); glVertex3f( 1.0 - dx, -1.0, z);
1732     glTexCoord2f(tx1, ty2); glVertex3f( 1.0 + dx, -1.0, z);
1733     fs_gl_color4f(s, s, s, s);
1734     glTexCoord2f(tx1, ty1); glVertex3f( 1.0 + dx, -0.5, z);
1735     glTexCoord2f(tx2, ty1); glVertex3f( 1.0 - dx, -0.5, z);
1736     glEnd();
1737 #endif
1738     CHECK_GL_ERROR();
1739     //printf("--- render glow done ---\n");
1740 }
1741 
render_fade_overlay(double alpha)1742 static void render_fade_overlay(double alpha)
1743 {
1744     // draw fading effect
1745     fs_gl_viewport(0, 0, fs_ml_video_width(), fs_ml_video_height());
1746     fs_gl_ortho_hd();
1747     fs_gl_blending(1);
1748     fs_gl_texturing(0);
1749     fs_gl_color4f(g_fs_emu_theme.fade_color[0] * alpha,
1750             g_fs_emu_theme.fade_color[1] * alpha,
1751             g_fs_emu_theme.fade_color[2] * alpha, alpha);
1752 
1753 #ifdef USE_GLES
1754     GLfloat vert[] = {
1755         0, 0,
1756         1920, 0,
1757         1920, 1080,
1758         0, 1080
1759     };
1760 
1761     glEnableClientState(GL_VERTEX_ARRAY);
1762 
1763     glVertexPointer(2, GL_FLOAT, 0, vert);
1764     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1765 
1766     glDisableClientState(GL_VERTEX_ARRAY);
1767 #else
1768     glBegin(GL_QUADS);
1769     glVertex2f(0, 0);
1770     glVertex2f(1920, 0);
1771     glVertex2f(1920, 1080);
1772     glVertex2f(0, 1080);
1773     glEnd();
1774 #endif
1775 
1776     CHECK_GL_ERROR();
1777 }
1778 
1779 /**
1780  * This function is called at the end of the frame rendering function
1781  */
handle_quit_sequence(void)1782 static void handle_quit_sequence(void)
1783 {
1784     int fade_time = fs_config_get_int_clamped("fade_out_duration", 0, 10000);
1785     if (fade_time == FS_CONFIG_NONE) {
1786         // fade out over 125ms
1787         fade_time = 125;
1788     }
1789     fade_time = fade_time * 1000;
1790     if (!fs_ml_fullscreen()) {
1791         fade_time = 0;
1792     }
1793 
1794     int64_t dt = fs_emu_monotonic_time() - g_fs_emu_quit_time;
1795     if (dt > fade_time && g_fs_emu_emulation_thread_stopped) {
1796         fs_emu_log("calling fs_ml_stop because emu thread is done\n");
1797         fs_ml_stop();
1798 
1799     }
1800     else if (dt > 5 * 1000 * 1000) {
1801         // 5 seconds has passed after shutdown was requested
1802         fs_emu_log("calling fs_ml_stop because emu does not stop\n");
1803         // FIXME: FORCE STOP
1804         fs_ml_stop();
1805         fs_emu_log("force-closing the emulator\n");
1806         exit(1);
1807     }
1808 
1809 
1810     if (fade_time > 0) {
1811         float fade = (1.0 * dt) / fade_time;
1812         if (fade > 1.0) {
1813             fade = 1.0;
1814         }
1815         render_fade_overlay(fade);
1816     }
1817 }
1818 
fs_emu_video_update_function()1819 int fs_emu_video_update_function()
1820 {
1821     return update_texture();
1822 }
1823 
fs_emu_video_render_function()1824 void fs_emu_video_render_function()
1825 {
1826     static int initialized_menu = 0;
1827     if (!initialized_menu) {
1828         // render menu once (without really showing it, so all menu
1829         // resources are initialized and loaded, -prevents flickering
1830         // when really opening the menu later
1831         fs_emu_menu_render(g_menu_transition);
1832         initialized_menu = 1;
1833     }
1834 
1835     // FIXME: MOVE TO DISPLAY FUNCTION, clear after flip so clearing can
1836     // be done asynchronously?
1837     glClear(GL_COLOR_BUFFER_BIT);
1838 
1839     if (g_fs_emu_video_debug) {
1840         int quarter_height = fs_ml_video_height() / 4;
1841         fs_gl_viewport(0, quarter_height, fs_ml_video_width(),
1842                 fs_ml_video_height() - quarter_height);
1843     } else {
1844         fs_gl_viewport(0, 0, fs_ml_video_width(), fs_ml_video_height());
1845     }
1846 
1847     // FIXME: can perhaps remove this soon..
1848     fs_emu_video_render_mutex_lock();
1849 
1850 
1851     int in_menu = fs_emu_menu_mode();
1852     if (in_menu && g_menu_transition_target < 1.0) {
1853         g_menu_transition_target = 1.0;
1854     }
1855     if (!in_menu && g_menu_transition_target > 0.0) {
1856         g_menu_transition_target = 0.0;
1857     }
1858 
1859     float menu_transition_speed = 1.0;
1860     int allow_perspective = 1;
1861 
1862 #ifdef WITH_LUA
1863     //fs_emu_acquire_lua();
1864     //lua_getglobal(fs_emu_lua_state, "on_render_frame");
1865     //if (lua_isnil(fs_emu_lua_state, -1)) {
1866     //    lua_pop(fs_emu_lua_state, 1);
1867     //}
1868 
1869     if (g_frame_override) {
1870         // don't allow perspective with the new scripted rendering system
1871         allow_perspective = 0;
1872         menu_transition_speed = 1.0 / 7.0;
1873         glClearColor(0.0, 0.0, 0.0, 1.0);
1874         glClear(GL_COLOR_BUFFER_BIT);
1875     }
1876 
1877     //fs_emu_release_lua();
1878 #endif
1879 
1880     // FIXME: ideally, we would use time-based animation - for now, we use a
1881     // simple frame-based animation
1882     if (g_menu_transition < g_menu_transition_target) {
1883         if (g_menu_transition_target == 1.0) {
1884             g_menu_transition += menu_transition_speed;
1885         }
1886     }
1887     if (g_menu_transition > g_menu_transition_target) {
1888         if (g_menu_transition_target == 0.0) {
1889             g_menu_transition -= menu_transition_speed;
1890         }
1891     }
1892     if (g_menu_transition > 1.0) {
1893         g_menu_transition = 1.0;
1894     } else if (g_menu_transition < 0.0) {
1895         g_menu_transition = 0.0;
1896     }
1897 
1898     int matrix_pushed = 0;
1899 
1900     double t0_x = 0.0;
1901     double t0_y = 0.0;
1902     double t0_z = -2.42;
1903     double r0_a = 0.0;
1904 
1905     double t1_x = -0.31;
1906     //double t1_y = -0.04;
1907     double t1_y = 0.0;
1908     double t1_z = -3.7;
1909     double r1_a = 30.0;
1910 
1911     int perspective = 0;
1912 
1913 #ifdef FLAT
1914     allow_perspective = 0;
1915 #endif
1916 
1917     if (g_menu_transition == 0.0 || allow_perspective == 0) {
1918         perspective = 0;
1919         fs_gl_ortho();
1920         //glTranslated(1920.0 / 2.0, 1080.0 / 2.0, 0.0);
1921         //glScaled(1920.0 / 2.0, 1080.0 / 2.0, 1.0);
1922     } else {
1923         perspective = 1;
1924 
1925         glClearColor(0.1, 0.1, 0.1, 1.0);
1926         glClear(GL_DEPTH_BUFFER_BIT);
1927         CHECK_GL_ERROR();
1928         fs_gl_ortho_hd();
1929 
1930         // transition y-coordinate between floor and wall
1931         int splt = g_fs_emu_theme.floor_height;
1932         fs_gl_blending(FALSE);
1933         fs_gl_texturing(FALSE);
1934 
1935 #ifdef USE_GLES
1936         GLfloat vert[] = {
1937             // q1
1938             0, splt, -0.9,
1939             1920, splt, -0.9,
1940             1920, 1020, -0.9,
1941             0, 1020, -0.9,
1942             // q2
1943             0, 1020, -0.9,
1944             1920, 1020, -0.9,
1945             1920, 1080, -0.9,
1946             0, 1080, -0.9,
1947             // q3
1948             0, 0, -0.9,
1949             1920, 0, -0.9,
1950             1920, splt, -0.9,
1951             0, splt, -0.9
1952         };
1953         GLfloat *wc1 = g_fs_emu_theme.wall_color_1;
1954         GLfloat *wc2 = g_fs_emu_theme.wall_color_2;
1955         GLfloat color[] = {
1956             // q1
1957             wc2[0], wc2[1], wc2[2], wc2[3],
1958             wc2[0], wc2[1], wc2[2], wc2[3],
1959             wc1[0], wc1[1], wc1[2], wc1[3],
1960             wc1[0], wc1[1], wc1[2], wc1[3],
1961             // q2
1962             wc1[0], wc1[1], wc1[2], wc1[3],
1963             wc1[0], wc1[1], wc1[2], wc1[3],
1964             wc1[0], wc1[1], wc1[2], wc1[3],
1965             wc1[0], wc1[1], wc1[2], wc1[3],
1966             // q3
1967             wc2[0], wc2[1], wc2[2], wc2[3],
1968             wc2[0], wc2[1], wc2[2], wc2[3],
1969             wc1[0], wc1[1], wc1[2], wc1[3],
1970             wc1[0], wc1[1], wc1[2], wc1[3],
1971         };
1972 
1973         glEnableClientState(GL_VERTEX_ARRAY);
1974         glEnableClientState(GL_COLOR_ARRAY);
1975 
1976         glVertexPointer(3, GL_FLOAT, 0, vert);
1977         glColorPointer(4, GL_FLOAT, 0, color);
1978         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1979         glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
1980         glDrawArrays(GL_TRIANGLE_FAN, 8, 4);
1981 
1982         glDisableClientState(GL_VERTEX_ARRAY);
1983         glDisableClientState(GL_COLOR_ARRAY);
1984 #else
1985         glBegin(GL_QUADS);
1986 
1987         fs_gl_color4fv(g_fs_emu_theme.wall_color_2);
1988         glVertex3f(   0, splt, -0.9);
1989         glVertex3f(1920, splt, -0.9);
1990         fs_gl_color4fv(g_fs_emu_theme.wall_color_1);
1991         glVertex3f(1920, 1020, -0.9);
1992         glVertex3f(   0, 1020, -0.9);
1993 
1994         glVertex3f(   0, 1020, -0.9);
1995         glVertex3f(1920, 1020, -0.9);
1996         //fs_gl_color4f(0.0, 0.0, 0.0, 1.0);
1997         glVertex3f(1920, 1080, -0.9);
1998         glVertex3f(   0, 1080, -0.9);
1999 
2000         fs_gl_color4fv(g_fs_emu_theme.floor_color_2);
2001         glVertex3f(   0,    0, -0.9);
2002         glVertex3f(1920,    0, -0.9);
2003         fs_gl_color4fv(g_fs_emu_theme.floor_color_1);
2004         glVertex3f(1920, splt, -0.9);
2005         glVertex3f(   0, splt, -0.9);
2006 
2007         glEnd();
2008 #endif
2009         CHECK_GL_ERROR();
2010 
2011         fs_gl_perspective();
2012         double t_x = t0_x + (t1_x - t0_x) * g_menu_transition;
2013         double t_y = t0_y + (t1_y - t0_y) * g_menu_transition;
2014         double t_z = t0_z + (t1_z - t0_z) * g_menu_transition;
2015         double r_a = r0_a + (r1_a - r0_a) * g_menu_transition;
2016 
2017         glPushMatrix();
2018         matrix_pushed = 1;
2019 
2020         glScaled(16.0 / 9.0, 1.0, 1.0);
2021         glTranslated(t_x, t_y, t_z);
2022         glRotated(r_a, 0.0, 1.0, 0.0);
2023         CHECK_GL_ERROR();
2024     }
2025 
2026     if (perspective) {
2027         render_glow(g_menu_transition);
2028     }
2029     if (perspective) {
2030         glPushMatrix();
2031         glTranslatef(0.0, -2.0, 0.0);
2032         //glTranslatef(0.0, -1.0, 0.0);
2033         //glScalef(1.0, -1.0, 1.0);
2034         glScalef(1.0, -0.5, 1.0);
2035         glTranslatef(0.0, -1.0, 0.0);
2036         CHECK_GL_ERROR();
2037         render_frame(0.33, perspective);
2038         CHECK_GL_ERROR();
2039         render_gloss(g_menu_transition * 0.66);
2040         CHECK_GL_ERROR();
2041         glPopMatrix();
2042         CHECK_GL_ERROR();
2043     }
2044 #ifdef FLAT
2045     if (g_menu_transition == 0) {
2046         render_frame(1.0, 0);
2047     } else {
2048         render_frame(1.0 - g_menu_transition * 0.66, 0);
2049     }
2050 #else
2051     render_frame(1.0, perspective);
2052     if (perspective) {
2053         render_gloss(g_menu_transition);
2054     }
2055 #endif
2056 #if 0
2057     if (fs_emu_is_paused()) {
2058         fs_gl_blending(1);
2059         fs_gl_texturing(1);
2060         fs_emu_draw_texture_with_size(TEXTURE_PAUSE, 0.0, 0.0, 0.1, 0.1);
2061     }
2062 #endif
2063 
2064     if (matrix_pushed) {
2065         glPopMatrix();
2066         CHECK_GL_ERROR();
2067         matrix_pushed = 0;
2068     }
2069 
2070     fs_emu_acquire_gui_lock();
2071     fse_render_hud();
2072 
2073     //if (fs_emu_menu_is_active()) {
2074     if (g_menu_transition > 0.0) {
2075         fs_emu_menu_render(g_menu_transition);
2076     }
2077 
2078     fs_emu_dialog_render();
2079     fs_emu_release_gui_lock();
2080 
2081 #ifdef WITH_NETPLAY
2082     if (g_fs_emu_hud_mode && fs_emu_netplay_enabled()) {
2083         fs_gl_ortho_hd();
2084         fs_gl_texturing(0);
2085         fs_gl_blending(1);
2086         fs_gl_color4f(0.0, 0.0, 0.0, 0.5);
2087 #ifdef USE_GLES
2088         GLfloat vert[] = {
2089             0, 1030,
2090             1920, 1030,
2091             1920, 1080,
2092             0, 1080
2093         };
2094         glEnableClientState(GL_VERTEX_ARRAY);
2095         glVertexPointer(2, GL_FLOAT, 0, vert);
2096         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
2097         glDisableClientState(GL_VERTEX_ARRAY);
2098 #else
2099         glBegin(GL_QUADS);
2100         glVertex2f(0, 1030);
2101         glVertex2f(1920, 1030);
2102         glVertex2f(1920, 1080);
2103         glVertex2f(0, 1080);
2104         glEnd();
2105 #endif
2106         CHECK_GL_ERROR();
2107 #if 0
2108         glBegin(GL_QUADS);
2109         glVertex2f(0, 1030);
2110         glVertex2f(1920, 1030);
2111         fs_gl_color4f(0.0, 0.0, 0.0, 0.0);
2112         glVertex2f(1920, 1030 - 50);
2113         glVertex2f(0, 1030 - 50);
2114         glEnd();
2115 #endif
2116         fs_emu_font *menu_font = fs_emu_font_get_menu();
2117         char *str;
2118 
2119         for (int i = 0; i < MAX_PLAYERS; i++) {
2120             fs_emu_player *player = g_fs_emu_players + i;
2121             int x = i * 1920 / 6 + 20;
2122             int y = 1038;
2123 
2124             int rendered_tag = 0;
2125             if (player->tag[0]) {
2126                 str = g_strdup_printf("%s", player->tag);
2127                 fs_emu_font_render(menu_font, str, x, y,
2128                         1.0, 1.0, 1.0, 1.0);
2129                 free(str);
2130                 rendered_tag = 1;
2131             }
2132             if (rendered_tag || player->ping) {
2133                 str = g_strdup_printf("%03d", player->ping);
2134                 fs_emu_font_render(menu_font, str, x + 100, y,
2135                         1.0, 1.0, 1.0, 1.0);
2136                 free(str);
2137             }
2138             /*
2139             if (rendered_tag || player->lag) {
2140                 str = g_strdup_printf("%03d", player->lag);
2141                 fs_emu_font_render(menu_font, str, x + 200, y,
2142                         1.0, 1.0, 1.0, 1.0);
2143                 free(str);
2144             }
2145             */
2146         }
2147     }
2148 #endif
2149 
2150 #if 1
2151     if (fs_emu_is_paused()) {
2152         fs_gl_ortho_hd();
2153         fs_gl_blending(1);
2154         fs_gl_texturing(1);
2155         fs_emu_draw_texture(TEXTURE_PAUSE, 160, 1080 - 160 - 64);
2156     }
2157 #endif
2158 
2159     if (g_fs_emu_video_debug) {
2160         int quarter_height = fs_ml_video_height() / 4;
2161         fs_gl_viewport(0, 0, fs_ml_video_width(), quarter_height);
2162         CHECK_GL_ERROR();
2163 
2164         fs_emu_set_texture(NULL);
2165         CHECK_GL_ERROR();
2166         static GLuint debug_texture = 0;
2167         static uint32_t *debug_texture_data = NULL;
2168         if (debug_texture == 0) {
2169             debug_texture_data = g_malloc0(256 * 256 * 4);
2170             glGenTextures(1, &debug_texture);
2171             CHECK_GL_ERROR();
2172             fs_gl_bind_texture(debug_texture);
2173             fs_gl_unpack_row_length(0);
2174             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0,
2175                     GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2176             CHECK_GL_ERROR();
2177             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
2178             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
2179             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
2180                     GL_CLAMP_TO_EDGE);
2181             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
2182                     GL_CLAMP_TO_EDGE);
2183             CHECK_GL_ERROR();
2184         }
2185         else {
2186             fs_gl_bind_texture(debug_texture);
2187             CHECK_GL_ERROR();
2188         }
2189 
2190         memset(debug_texture_data, 0x00, 256 * 256 * 4);
2191         CHECK_GL_ERROR();
2192         fs_emu_video_render_debug_info(debug_texture_data);
2193         CHECK_GL_ERROR();
2194         fs_emu_audio_render_debug_info(debug_texture_data);
2195         CHECK_GL_ERROR();
2196 
2197         fs_gl_unpack_row_length(0);
2198         CHECK_GL_ERROR();
2199         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 256,
2200                 GL_RGBA, GL_UNSIGNED_BYTE, debug_texture_data);
2201         CHECK_GL_ERROR();
2202         fs_gl_ortho_hd();
2203         fs_gl_texturing(1);
2204         fs_gl_blending(0);
2205         fs_gl_color4f(1.0, 1.0, 1.0, 1.0);
2206 #ifdef USE_GLES
2207         GLfloat tex[] = {
2208             0.0, 0.0,
2209             1.0, 0.0,
2210             1.0, 1.0,
2211             0.0, 1.0
2212         };
2213         GLfloat vert[] = {
2214             0, 0,
2215             1920, 0,
2216             1920, 1080,
2217             0, 1080
2218         };
2219 
2220         glEnableClientState(GL_VERTEX_ARRAY);
2221         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
2222 
2223         glTexCoordPointer(2, GL_FLOAT, 0, tex);
2224         glVertexPointer(2, GL_FLOAT, 0, vert);
2225         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
2226 
2227         glDisableClientState(GL_VERTEX_ARRAY);
2228         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
2229 #else
2230         glBegin(GL_QUADS);
2231         glTexCoord2f(0.0, 0.0);
2232         glVertex2f(0, 0);
2233         glTexCoord2f(1.0, 0.0);
2234         glVertex2f(1920, 0);
2235         glTexCoord2f(1.0, 1.0);
2236         glVertex2f(1920, 1080);
2237         glTexCoord2f(0.0, 1.0);
2238         glVertex2f(0, 1080);
2239         glEnd();
2240 #endif
2241         CHECK_GL_ERROR();
2242 
2243         glPushMatrix();
2244         glScalef(1.0, 4.0, 1.0);
2245 
2246         fs_emu_font *menu_font = fs_emu_font_get_menu();
2247         char *str;
2248 
2249         /*
2250         str = g_strdup_printf("%d", fs_emu_get_audio_frequency());
2251         fs_emu_font_render(menu_font, str, 1920 / 2 + 20, 3,
2252                 1.0, 1.0, 1.0, 1.0);
2253         free(str);
2254         */
2255 
2256         static double avg_emu = 0.0;
2257         static double avg_sys = 0.0;
2258         static double avg_fill = 0.0;
2259         static int avg_throttle = 0;
2260         if (avg_throttle == 0) {
2261             avg_emu = fs_emu_get_average_emu_fps();
2262             avg_sys = fs_emu_get_average_sys_fps();
2263             avg_fill = fs_emu_audio_get_measured_avg_buffer_fill(0);
2264             avg_throttle = 10;
2265         }
2266         avg_throttle -= 1;
2267 
2268         str = g_strdup_printf("%0.1f", avg_fill / 1000.0);
2269         fs_emu_font_render(menu_font, str, 1920 / 2 + 220, 3,
2270                 1.0, 1.0, 1.0, 1.0);
2271         free(str);
2272         str = g_strdup_printf("%d", g_fs_emu_audio_buffer_underruns);
2273         fs_emu_font_render(menu_font, str, 1920 / 2 + 420, 3,
2274                 1.0, 1.0, 1.0, 1.0);
2275         free(str);
2276 
2277         fs_emu_font_render(menu_font, "EMU", 20, 3, 1.0, 1.0, 1.0, 1.0);
2278         str = g_strdup_printf("%0.2f", avg_emu);
2279         fs_emu_font_render(menu_font, str, 220, 3, 1.0, 1.0, 1.0, 1.0);
2280         free(str);
2281         str = g_strdup_printf("%d", g_fs_emu_lost_frames);
2282         fs_emu_font_render(menu_font, str, 420, 3, 1.0, 1.0, 1.0, 1.0);
2283         free(str);
2284         str = g_strdup_printf("%d", g_fs_emu_repeated_frames);
2285         fs_emu_font_render(menu_font, str, 620, 3, 1.0, 1.0, 1.0, 1.0);
2286         free(str);
2287 
2288         fs_emu_font_render(menu_font, "SYS", 20, 140, 1.0, 1.0, 1.0, 1.0);
2289         str = g_strdup_printf("%0.2f", avg_sys);
2290         fs_emu_font_render(menu_font, str, 220, 140, 1.0, 1.0, 1.0, 1.0);
2291         free(str);
2292         str = g_strdup_printf("%d", g_fs_emu_lost_vblanks);
2293         fs_emu_font_render(menu_font, str, 420, 140, 1.0, 1.0, 1.0, 1.0);
2294         free(str);
2295 
2296         glPopMatrix();
2297         CHECK_GL_ERROR();
2298     }
2299 
2300     if (fs_emu_is_quitting()) {
2301         handle_quit_sequence();
2302     }
2303 
2304     static int64_t start_time = -1;
2305     if (start_time == -1) {
2306         start_time = fs_emu_monotonic_time();
2307     }
2308     static int fade_time = FS_CONFIG_NONE;
2309     if (fade_time == FS_CONFIG_NONE) {
2310         fade_time = fs_config_get_int_clamped("fade_in_duration", 0, 10000);
2311         if (fade_time == FS_CONFIG_NONE) {
2312             fade_time = 500;
2313         }
2314         fade_time = fade_time * 1000;
2315         if (!fs_ml_fullscreen()) {
2316             fade_time = 0;
2317         }
2318     }
2319 
2320     uint64_t dt = fs_emu_monotonic_time() - start_time;
2321     if (dt < fade_time) {
2322         float fade = 1.0 - (1.0 * dt) / fade_time;
2323         if (fade < 0.0) {
2324             fade = 0.0;
2325         }
2326         render_fade_overlay(fade);
2327     }
2328 
2329     fs_emu_video_render_mutex_unlock();
2330 }
2331 
fs_emu_video_render_debug_info(uint32_t * texture)2332 void fs_emu_video_render_debug_info(uint32_t *texture)
2333 {
2334     //return;
2335     int x;
2336     //, y;
2337     int y1;
2338     GList *link;
2339     uint32_t color = 0x80404080;
2340     fs_gl_ortho_hd();
2341     fs_gl_blending(TRUE);
2342     fs_gl_texturing(FALSE);
2343 
2344     // render debug triangles, these are for visually debugging vblank
2345     // synchronization
2346 
2347     y1 = 0;
2348     color = 0x80800080;
2349     static int start_add = 0;
2350     int add = start_add;
2351     for (int x = 0; x < 256; x++) {
2352         int y2 = add % 20;
2353         for (int y = y1; y < y2; y++) {
2354             *(texture + (((255 - y) * 256) + x)) = color;
2355         }
2356         add += 2;
2357     }
2358     start_add += 2;
2359 
2360     x = 127;
2361     y1 = 128;
2362     color = 0x80404080;
2363     link = g_queue_peek_head_link(g_fs_emu_sys_frame_times.queue);
2364     while (link) {
2365         int val = FS_POINTER_TO_INT(link->data);
2366         //int x2 = x - 8;
2367         int y2 = y1 + val * VIDEO_DEBUG_SCALE_TIMES;
2368         if (y2 > 256) {
2369             y2 = 256;
2370         }
2371         for (int y = y1; y < y2; y++) {
2372             *(texture + ((y * 256) + x)) = color;
2373         }
2374         if (--x < 0) {
2375             break;
2376         }
2377         link = link->next;
2378     }
2379 
2380     x = 127;
2381     y1 = 0;
2382     color = 0x80205080;
2383     link = g_queue_peek_head_link(g_fs_emu_emu_frame_times.queue);
2384     while (link) {
2385         int val = FS_POINTER_TO_INT(link->data);
2386         //int x2 = x - 8;
2387         int y2 = y1 + val * VIDEO_DEBUG_SCALE_TIMES;
2388         if (y2 > 256) {
2389             y2 = 256;
2390         }
2391         for (int y = y1; y < y2; y++) {
2392             *(texture + ((y * 256) + x)) = color;
2393         }
2394         if (--x < 0) {
2395             break;
2396         }
2397         link = link->next;
2398     }
2399 
2400     x = 127;
2401     y1 = 0;
2402     color = 0x80008080;
2403     link = g_queue_peek_head_link(g_fs_emu_emu2_frame_times.queue);
2404     while (link) {
2405         int val = FS_POINTER_TO_INT(link->data);
2406         //int x2 = x - 8;
2407         int y2 = y1 + val * VIDEO_DEBUG_SCALE_TIMES;
2408         if (y2 > 256) {
2409             y2 = 256;
2410         }
2411         for (int y = y1; y < y2; y++) {
2412             *(texture + ((y * 256) + x)) = color;
2413         }
2414         if (--x < 0) {
2415             break;
2416         }
2417         link = link->next;
2418     }
2419 }
2420 
2421 #ifdef WITH_LUA
2422 
l_fs_emu_set_scale(lua_State * L)2423 static int l_fs_emu_set_scale(lua_State *L)
2424 {
2425     int x = luaL_checkinteger(L, 1);
2426     int y = luaL_checkinteger(L, 2);
2427     // make sure we don't divide by zero later, zero scale not allowed
2428     if (x == 0) {
2429         x = 1;
2430     }
2431     if (y == 0) {
2432         y = 1;
2433     }
2434     g_l_scale_x = x;
2435     g_l_scale_y = y;
2436 
2437     return 0;
2438 }
2439 
2440 /*
2441 static int l_fs_emu_render_frame(lua_State *L) {
2442     int x = luaL_checkinteger(L, 1);
2443     int y = luaL_checkinteger(L, 2);
2444     int w = luaL_checkinteger(L, 3);
2445     int h = luaL_checkinteger(L, 4);
2446 
2447     float color = 1.0;
2448     if (g_frame_texture == 0) {
2449         // texture has not been created yet
2450         color = 0.0;
2451         fs_gl_texturing(0);
2452     }
2453     else {
2454         fs_gl_texturing(1);
2455         fs_gl_bind_texture(g_frame_texture);
2456     }
2457     fs_gl_blending(0);
2458     fs_gl_color4f(color, color, color, 1.0);
2459 
2460     float x1 = -1.0 + x * 2.0 / g_l_scale_x;
2461     float x2 = -1.0 + (x + w) * 2.0 / g_l_scale_x;
2462     float y2 = 1.0 - y * 2.0 / g_l_scale_y;
2463     float y1 = 1.0 - (y + h) * 2.0 / g_l_scale_y;
2464 
2465     double s1 = 0.0;
2466     double t1 = 0.0;
2467     double s2 = (double) g_crop.w / g_frame_texture_width;
2468     double t2 = (double) g_crop.h / g_frame_texture_height;
2469 
2470     render_quad(x1, y1, x2, y2, s1, t1, s2, t2);
2471 
2472     return 0;
2473 }
2474 */
2475 
l_fs_emu_set_frame_position_and_size(lua_State * L)2476 static int l_fs_emu_set_frame_position_and_size(lua_State *L) {
2477     int x = luaL_checkinteger(L, 1);
2478     int y = luaL_checkinteger(L, 2);
2479     int w = luaL_checkinteger(L, 3);
2480     int h = luaL_checkinteger(L, 4);
2481 
2482     g_frame_override_x = x;
2483     g_frame_override_y = y;
2484     g_frame_override_w = w;
2485     g_frame_override_h = h;
2486 
2487     g_frame_override = g_frame_override_w > 0 || g_frame_override_h > 0 ||
2488             g_frame_override_x > 0 || g_frame_override_y > 0;
2489 
2490     return 0;
2491 }
2492 
fs_emu_render_init_lua(void)2493 void fs_emu_render_init_lua(void)
2494 {
2495     fs_log("fs_emu_render_init_lua\n");
2496 
2497     //lua_register(fs_emu_lua_state, "fs_emu_render_frame",
2498     //        l_fs_emu_render_frame);
2499     lua_register(fs_emu_lua_state, "fs_emu_set_scale",
2500             l_fs_emu_set_scale);
2501     lua_register(fs_emu_lua_state, "fs_emu_set_frame_position_and_size",
2502             l_fs_emu_set_frame_position_and_size);
2503 }
2504 
2505 #endif
2506 
fse_init_renderer()2507 void fse_init_renderer()
2508 {
2509     fs_log("fse_init_renderer\n");
2510     fse_init_render();
2511 
2512     g_scale_x = fs_config_get_double("scale_x");
2513     if (g_scale_x == FS_CONFIG_NONE) {
2514         g_scale_x = -1.0;
2515     }
2516     g_scale_y = fs_config_get_double("scale_y");
2517     if (g_scale_y == FS_CONFIG_NONE) {
2518         g_scale_y = -1.0;
2519     }
2520     g_align_x = fs_config_get_double("align_x");
2521     if (g_align_x == FS_CONFIG_NONE) {
2522         g_align_x = 0.5;
2523     }
2524     g_align_y = fs_config_get_double("align_y");
2525     if (g_align_y == FS_CONFIG_NONE) {
2526         g_align_y = 0.5;
2527     }
2528 
2529     const char *ccstr = fs_config_get_const_string("texture_filter");
2530     if (ccstr && g_ascii_strcasecmp(ccstr, "nearest") == 0) {
2531         g_texture_filter = GL_NEAREST;
2532     }
2533 
2534     fs_log("scale: %0.2f %0.2f align: %0.2f %0.2f\n", g_scale_x, g_scale_y,
2535             g_align_x, g_align_y);
2536 
2537     g_overlay_mutex = fs_mutex_create();
2538 
2539 #ifdef WITH_LUA
2540     fs_emu_render_init_lua();
2541 #endif
2542 }
2543