Quellcode durchsuchen

libretro, fix memory leak
sms, add ghosting for GG LCD (generic+libretro only)

root vor 2 Jahren
Ursprung
Commit
92b80c5bb2

+ 1 - 0
platform/common/emu.h

@@ -90,6 +90,7 @@ typedef struct _currentConfig_t {
 	int renderer;
 	int renderer32x;
 	int filter;  // EOPT_FILTER_* video filter
+	int ghosting;
 	int analog_deadzone;
 	int msh2_khz;
 	int ssh2_khz;

+ 1 - 0
platform/common/menu_pico.h

@@ -152,6 +152,7 @@ typedef enum
 	MA_32XOPT_SSH2_CYCLES,
 	MA_SMSOPT_HARDWARE,
 	MA_SMSOPT_MAPPER,
+	MA_SMSOPT_GHOSTING,
 	MA_CTRL_PLAYER1,
 	MA_CTRL_PLAYER2,
 	MA_CTRL_EMU,

+ 34 - 5
platform/common/upscale.h

@@ -36,16 +36,16 @@
 #include <pico/pico_types.h>
 
 
-/* LSB of all colors in a pixel */
+/* LSB of all colors in 1 or 2 pixels */
 #if defined(USE_BGR555)
-#define PXLSB		0x0421
+#define PXLSB		0x04210421
 #else
-#define PXLSB		0x0821
+#define PXLSB		0x08210821
 #endif
 
 /* RGB565 pixel mixing, see https://www.compuphase.com/graphic/scale3.htm and
   			    http://blargg.8bitalley.com/info/rgb_mixing.html */
-/* 2-level mixing */
+/* 2-level mixing. NB blargg version isn't 2-pixel-at-once safe for RGB565 */
 //#define p_05(d,p1,p2)	d=(((p1)+(p2)  + ( ((p1)^(p2))&PXLSB))>>1) // round up
 //#define p_05(d,p1,p2)	d=(((p1)+(p2)  - ( ((p1)^(p2))&PXLSB))>>1) // round down
 #define p_05(d,p1,p2)	d=(((p1)&(p2)) + ((((p1)^(p2))&~PXLSB)>>1))
@@ -379,7 +379,7 @@ scalers v:
 */
 
 #define v_mix(di,li,ri,w,p_mix,f) do {			\
-	u16 i, t, u; (void)t, (void)u;			\
+	int i; u32 t, u; (void)t, (void)u;		\
 	for (i = 0; i < w; i += 4) {			\
 		p_mix((di)[i  ], f((li)[i  ]),f((ri)[i  ])); \
 		p_mix((di)[i+1], f((li)[i+1]),f((ri)[i+1])); \
@@ -587,6 +587,35 @@ scalers v:
 } while (0)
 
 
+/* exponentially smoothing (for LCD ghosting): y[n] = x[n]*a + y[n-1]*(1-a) */
+
+#define PXLSBn (PXLSB*15) // using 4 LSBs of each subpixel for subtracting
+// NB implement rounding to x[n] by adding 1 to counter round down if y[n] is
+// smaller than x[n]: use some of the lower bits to implement subtraction on
+// subpixels, with an additional bit to detect borrow, then add the borrow.
+// It's doing the increment wrongly in a lot of cases, which doesn't matter
+// much since it will converge to x[n] in a few frames anyway if x[n] is static
+#define p_05_round(d,p1,p2)				\
+	p_05(u, p1, p2);				\
+	t=(u|~PXLSBn)-(p1&PXLSBn); d = u+(~(t>>4)&PXLSB)
+// Unfortunately this won't work for p_025, where adding 1 isn't enough and
+// adding 2 would be too much, so offer only p_075 here
+#define p_075_round(d,p1,p2)				\
+	p_075(u, p1, p2);				\
+	t=(u|~PXLSBn)-(p1&PXLSBn); d = u+(~(t>>4)&PXLSB)
+
+// this is essentially v_mix and v_copy combined
+#define v_blend(di,ri,w,p_mix) do {			\
+	int i; u32 t, u; (void)t, (void)u;		\
+	for (i = 0; i < w; i += 4) {			\
+		p_mix((ri)[i  ], (di)[i  ],(ri)[i  ]); (di)[i  ] = (ri)[i  ]; \
+		p_mix((ri)[i+1], (di)[i+1],(ri)[i+1]); (di)[i+1] = (ri)[i+1]; \
+		p_mix((ri)[i+2], (di)[i+2],(ri)[i+2]); (di)[i+2] = (ri)[i+2]; \
+		p_mix((ri)[i+3], (di)[i+3],(ri)[i+3]); (di)[i+3] = (ri)[i+3]; \
+	}						\
+} while (0)
+
+
 /* X x Y -> X*5/4 x Y, for X 256->320 */
 void upscale_rgb_nn_x_4_5(u16 *__restrict di, int ds, u8 *__restrict si, int ss, int width, int height, u16 *pal);
 void upscale_rgb_snn_x_4_5(u16 *__restrict di, int ds, u8 *__restrict si, int ss, int width, int height, u16 *pal);

+ 49 - 3
platform/libretro/libretro.c

@@ -40,6 +40,8 @@
 #include <malloc.h>
 #include "libretro-common/include/libretro_gskit_ps2.h"
 #include "ps2/asm.h"
+#else
+#include <platform/common/upscale.h>
 #endif
 
 #ifdef _3DS
@@ -93,7 +95,6 @@ static const float VOUT_4_3 = (4.0f / 3.0f);
 static const float VOUT_CRT = (1.29911f);
 
 static bool show_overscan = false;
-static bool old_show_overscan = false;
 
 /* Required to allow on the fly changes to 'show overscan' */
 static int vm_current_start_line = -1;
@@ -103,9 +104,10 @@ static int vm_current_col_count = -1;
 
 static int vout_16bit = 1;
 static int vout_format = PDF_RGB555;
-static void *vout_buf;
+static void *vout_buf, *vout_ghosting_buf;
 static int vout_width, vout_height, vout_offset;
 static float vout_aspect = 0.0;
+static int vout_ghosting = 0;
 
 #if defined(RENDER_GSKIT_PS2)
 #define VOUT_8BIT_WIDTH 328
@@ -672,6 +674,12 @@ void emu_video_mode_change(int start_line, int line_count, int start_col, int co
          VOUT_MAX_HEIGHT : vout_height;
    vout_offset = (vout_offset > vout_width * (VOUT_MAX_HEIGHT - 1) * 2) ?
          vout_width * (VOUT_MAX_HEIGHT - 1) * 2 : vout_offset;
+
+   /* LCD ghosting */
+   if (vout_ghosting && vout_height == 144) {
+      vout_ghosting_buf = realloc(vout_ghosting_buf, VOUT_MAX_HEIGHT*vout_width*2);
+      memset(vout_ghosting_buf, 0, vout_width*vout_height*2);
+   }
 #endif
    Pico.m.dirtyPal = 1;
 
@@ -1443,6 +1451,7 @@ static void update_variables(bool first_run)
    double new_sound_rate;
    unsigned short old_snd_filter;
    int32_t old_snd_filter_range;
+   bool old_show_overscan;
 
    var.value = NULL;
    var.key = "picodrive_input1";
@@ -1506,6 +1515,17 @@ static void update_variables(bool first_run)
          PicoIn.hwSelect = PMS_MAP_SEGA;
    }
 
+   var.value = NULL;
+   var.key = "picodrive_ggghost";
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
+      if (strcmp(var.value, "normal") == 0)
+         vout_ghosting = 2;
+      else if (strcmp(var.value, "weak") == 0)
+         vout_ghosting = 1;
+      else
+         vout_ghosting = 0;
+   }
+
    OldPicoRegionOverride = PicoIn.regionOverride;
    var.value = NULL;
    var.key = "picodrive_region";
@@ -1818,6 +1838,20 @@ void retro_run(void)
       }
    }
 
+   if (vout_ghosting && vout_height == 144) {
+      unsigned short *pd = (unsigned short *)vout_buf;
+      unsigned short *ps = (unsigned short *)vout_ghosting_buf;
+      int y;
+      for (y = 0; y < VOUT_MAX_HEIGHT; y++) {
+         if (vout_ghosting == 1)
+            v_blend(pd, ps, vout_width, p_075_round);
+         else
+            v_blend(pd, ps, vout_width, p_05_round);
+         pd += vout_width;
+         ps += vout_width;
+      }
+   }
+
    buff = (char*)vout_buf + vout_offset;
 #endif
 
@@ -1854,7 +1888,6 @@ void retro_init(void)
       | POPT_EN_PSG|POPT_EN_Z80|POPT_EN_GG_LCD
       | POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX
       | POPT_EN_32X|POPT_EN_PWM
-//|POPT_DIS_IDLE_DET
       | POPT_ACC_SPRITES|POPT_DIS_32C_BORDER;
 #ifdef __arm__
 #ifdef _3DS
@@ -1902,6 +1935,8 @@ void retro_init(void)
 
 void retro_deinit(void)
 {
+   size_t i;
+
 #ifdef _3DS
    linearFree(vout_buf);
 #elif defined(RENDER_GSKIT_PS2)
@@ -1912,7 +1947,18 @@ void retro_deinit(void)
    free(vout_buf);
 #endif
    vout_buf = NULL;
+   if (vout_ghosting_buf)
+      free(vout_ghosting_buf);
+   vout_ghosting_buf = NULL;
+
    PicoExit();
 
+   for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++) {
+      if (disks[i].fname != NULL) {
+         free(disks[i].fname);
+         disks[i].fname = NULL;
+      }
+   }
+
    libretro_supports_bitmasks = false;
 }

+ 12 - 0
platform/libretro/libretro_core_options.h

@@ -125,6 +125,18 @@ struct retro_core_option_definition option_defs_us[] = {
        },
       "Auto"
    },
+   {
+      "picodrive_ggghost",
+      "Game Gear LCD ghosting",
+      "Enable LCD ghosting emulation.",
+      {
+         { "off", NULL },
+         { "weak",  NULL },
+         { "normal",  NULL },
+         { NULL, NULL },
+      },
+      "off"
+   },
    {
       "picodrive_region",
       "Region",

+ 41 - 8
platform/linux/emu.c

@@ -27,6 +27,7 @@ enum renderer_types { RT_16BIT, RT_8BIT_ACC, RT_8BIT_FAST, RT_COUNT };
 static int out_x, out_y, out_w, out_h;	// renderer output in render buffer
 static int screen_x, screen_y, screen_w, screen_h; // final render destination 
 static int render_bg;			// force 16bit mode for bg render
+static u16 *ghost_buf;			// backbuffer to simulate LCD ghosting
 
 void pemu_prep_defconfig(void)
 {
@@ -167,6 +168,23 @@ void pemu_finalize_frame(const char *fps, const char *notice)
 		screen_blit(pd, g_screen_ppitch, ps, 328, Pico.est.HighPal);
 	}
 
+	if (currentConfig.ghosting && out_h == 144) {
+		// GG LCD ghosting emulation
+		u16 *pd = screen_buffer(g_screen_ptr) +
+				out_y * g_screen_ppitch + out_x;
+		u16 *ps = ghost_buf;
+		int y, h = currentConfig.vscaling == EOPT_SCALE_SW ? 240:out_h;
+		int w = currentConfig.scaling == EOPT_SCALE_SW ? 320:out_w;
+		for (y = 0; y < h; y++) {
+			if (currentConfig.ghosting == 1)
+				v_blend((u32 *)pd, (u32 *)ps, w/2, p_075_round);
+			else
+				v_blend((u32 *)pd, (u32 *)ps, w/2, p_05_round);
+			pd += g_screen_ppitch;
+			ps += w;
+		}
+	}
+
 //#define FUNKEY_AUTHORIZE_TEXT_OVERLAY
 #ifndef FUNKEY_AUTHORIZE_TEXT_OVERLAY
 	return;
@@ -318,27 +336,30 @@ static int cb_vscaling_nop(unsigned int line)
 
 static int cb_vscaling_end(unsigned int line)
 {
-	u16 *dest = Pico.est.DrawLineDest;
+	u16 *dest = (u16 *)Pico.est.DrawLineDest + out_x;
+	// helpers for 32 bit operation (2 pixels at once):
+	u32 *dest32 = (u32 *)dest;
+	int pp = g_screen_ppitch;
 
 	if (out_h == 144)
 	  switch (currentConfig.filter) {
-	  case 0: v_upscale_nn_3_5(dest, g_screen_ppitch, 320, vscale_state);
+	  case 0: v_upscale_nn_3_5(dest32, pp/2, out_w/2, vscale_state);
 		  break;
-	  default: v_upscale_snn_3_5(dest, g_screen_ppitch, 320, vscale_state);
+	  default: v_upscale_snn_3_5(dest32, pp/2, out_w/2, vscale_state);
 		  break;
 	  }
 	else
 	  switch (currentConfig.filter) {
-	  case 3: v_upscale_bl4_16_17(dest, g_screen_ppitch, 320, vscale_state);
+	  case 3: v_upscale_bl4_16_17(dest32, pp/2, out_w/2, vscale_state);
 		  break;
-	  case 2: v_upscale_bl2_16_17(dest, g_screen_ppitch, 320, vscale_state);
+	  case 2: v_upscale_bl2_16_17(dest32, pp/2, out_w/2, vscale_state);
 		  break;
-	  case 1: v_upscale_snn_16_17(dest, g_screen_ppitch, 320, vscale_state);
+	  case 1: v_upscale_snn_16_17(dest32, pp/2, out_w/2, vscale_state);
 		  break;
-	  default: v_upscale_nn_16_17(dest, g_screen_ppitch, 320, vscale_state);
+	  default: v_upscale_nn_16_17(dest32, pp/2, out_w/2, vscale_state);
 		  break;
 	  }
-	Pico.est.DrawLineDest = dest;
+	Pico.est.DrawLineDest = (u16 *)dest32 - out_x;
 	return 0;
 }
 
@@ -388,6 +409,14 @@ void emu_video_mode_change(int start_line, int line_count, int start_col, int co
 		plat_video_set_size(screen_w, screen_h);
 	plat_video_set_buffer(g_screen_ptr);
 
+	// create a backing buffer for emulating the bad GG lcd display
+	if (currentConfig.ghosting && out_h == 144) {
+		int h = currentConfig.vscaling == EOPT_SCALE_SW ? 240:out_h;
+		int w = currentConfig.scaling == EOPT_SCALE_SW ? 320:out_w;
+		ghost_buf = realloc(ghost_buf, w * h * 2);
+		memset(ghost_buf, 0, w * h * 2);
+	}
+
 	// clear whole screen in all buffers
 	if (!is_16bit_mode())
 		memset32(Pico.est.Draw2FB, 0xe0e0e0e0, (320+8) * (8+240+8) / 4);
@@ -404,6 +433,10 @@ void pemu_loop_end(void)
 {
 	/* do one more frame for menu bg */
 	pemu_forced_frame(0, 1);
+	if (ghost_buf) {
+		free(ghost_buf);
+		ghost_buf = NULL;
+	}
 }
 
 void plat_wait_till_us(unsigned int us_to)

+ 6 - 3
platform/linux/menu.c

@@ -2,13 +2,16 @@
 
 static const char *men_scaling_opts[] = { "OFF", "software", "hardware", NULL };
 static const char *men_filter_opts[] = { "nearest", "smoother", "bilinear 1", "bilinear 2", NULL };
+static const char *men_ghosting_opts[] = { "OFF", "weak", "normal", NULL };
 
 static const char h_scale[] = "hardware scaling may not be working on some devices";
+static const char h_ghost[] = "when active simulates inertia of the GG LCD display";
 
 #define MENU_OPTIONS_GFX \
-	mee_enum_h    ("Horizontal scaling", MA_OPT_SCALING, currentConfig.scaling, men_scaling_opts, h_scale), \
-	mee_enum_h    ("Vertical scaling",  MA_OPT_VSCALING, currentConfig.vscaling, men_scaling_opts, h_scale), \
-	mee_enum_h    ("Scaler type", MA_OPT3_FILTERING, currentConfig.filter, men_filter_opts, NULL), \
+	mee_enum_h    ("Horizontal scaling",     MA_OPT_SCALING, currentConfig.scaling, men_scaling_opts, h_scale), \
+	mee_enum_h    ("Vertical scaling",       MA_OPT_VSCALING, currentConfig.vscaling, men_scaling_opts, h_scale), \
+	mee_enum_h    ("Scaler type",            MA_OPT3_FILTERING, currentConfig.filter, men_filter_opts, NULL), \
+	mee_enum_h    ("Game Gear LCD ghosting", MA_SMSOPT_GHOSTING, currentConfig.ghosting, men_ghosting_opts, h_ghost), \
 
 #define MENU_OPTIONS_ADV