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