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

This commit is contained in:
kub 2021-11-04 20:33:28 +01:00
parent 27005bdb5f
commit f55ce7bf47
7 changed files with 135 additions and 18 deletions

View file

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

View file

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

View file

@ -36,16 +36,16 @@
#include <pico/pico_types.h> #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) #if defined(USE_BGR555)
#define PXLSB 0x0421 #define PXLSB 0x04210421
#else #else
#define PXLSB 0x0821 #define PXLSB 0x08210821
#endif #endif
/* RGB565 pixel mixing, see https://www.compuphase.com/graphic/scale3.htm and /* RGB565 pixel mixing, see https://www.compuphase.com/graphic/scale3.htm and
http://blargg.8bitalley.com/info/rgb_mixing.html */ 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 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) // round down
#define p_05(d,p1,p2) d=(((p1)&(p2)) + ((((p1)^(p2))&~PXLSB)>>1)) #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 { \ #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) { \ for (i = 0; i < w; i += 4) { \
p_mix((di)[i ], f((li)[i ]),f((ri)[i ])); \ p_mix((di)[i ], f((li)[i ]),f((ri)[i ])); \
p_mix((di)[i+1], f((li)[i+1]),f((ri)[i+1])); \ p_mix((di)[i+1], f((li)[i+1]),f((ri)[i+1])); \
@ -587,6 +587,35 @@ scalers v:
} while (0) } 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 */ /* 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_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); void upscale_rgb_snn_x_4_5(u16 *__restrict di, int ds, u8 *__restrict si, int ss, int width, int height, u16 *pal);

View file

@ -40,6 +40,8 @@
#include <malloc.h> #include <malloc.h>
#include "libretro-common/include/libretro_gskit_ps2.h" #include "libretro-common/include/libretro_gskit_ps2.h"
#include "ps2/asm.h" #include "ps2/asm.h"
#else
#include <platform/common/upscale.h>
#endif #endif
#ifdef _3DS #ifdef _3DS
@ -93,7 +95,6 @@ static const float VOUT_4_3 = (4.0f / 3.0f);
static const float VOUT_CRT = (1.29911f); static const float VOUT_CRT = (1.29911f);
static bool show_overscan = false; static bool show_overscan = false;
static bool old_show_overscan = false;
/* Required to allow on the fly changes to 'show overscan' */ /* Required to allow on the fly changes to 'show overscan' */
static int vm_current_start_line = -1; 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_16bit = 1;
static int vout_format = PDF_RGB555; 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 int vout_width, vout_height, vout_offset;
static float vout_aspect = 0.0; static float vout_aspect = 0.0;
static int vout_ghosting = 0;
#if defined(RENDER_GSKIT_PS2) #if defined(RENDER_GSKIT_PS2)
#define VOUT_8BIT_WIDTH 328 #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_MAX_HEIGHT : vout_height;
vout_offset = (vout_offset > vout_width * (VOUT_MAX_HEIGHT - 1) * 2) ? vout_offset = (vout_offset > vout_width * (VOUT_MAX_HEIGHT - 1) * 2) ?
vout_width * (VOUT_MAX_HEIGHT - 1) * 2 : vout_offset; 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 #endif
Pico.m.dirtyPal = 1; Pico.m.dirtyPal = 1;
@ -1443,6 +1451,7 @@ static void update_variables(bool first_run)
double new_sound_rate; double new_sound_rate;
unsigned short old_snd_filter; unsigned short old_snd_filter;
int32_t old_snd_filter_range; int32_t old_snd_filter_range;
bool old_show_overscan;
var.value = NULL; var.value = NULL;
var.key = "picodrive_input1"; var.key = "picodrive_input1";
@ -1506,6 +1515,17 @@ static void update_variables(bool first_run)
PicoIn.hwSelect = PMS_MAP_SEGA; 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; OldPicoRegionOverride = PicoIn.regionOverride;
var.value = NULL; var.value = NULL;
var.key = "picodrive_region"; 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; buff = (char*)vout_buf + vout_offset;
#endif #endif
@ -1913,6 +1947,10 @@ void retro_deinit(void)
free(vout_buf); free(vout_buf);
#endif #endif
vout_buf = NULL; vout_buf = NULL;
if (vout_ghosting_buf)
free(vout_ghosting_buf);
vout_ghosting_buf = NULL;
PicoExit(); PicoExit();
for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++) { for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++) {

View file

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

View file

@ -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 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 screen_x, screen_y, screen_w, screen_h; // final render destination
static int render_bg; // force 16bit mode for bg render static int render_bg; // force 16bit mode for bg render
static u16 *ghost_buf; // backbuffer to simulate LCD ghosting
void pemu_prep_defconfig(void) 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); 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;
}
}
if (notice) if (notice)
emu_osd_text16(4, g_screen_height - 8, notice); emu_osd_text16(4, g_screen_height - 8, notice);
if (currentConfig.EmuOpt & EOPT_SHOW_FPS) if (currentConfig.EmuOpt & EOPT_SHOW_FPS)
@ -312,27 +330,30 @@ static int cb_vscaling_nop(unsigned int line)
static int cb_vscaling_end(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) if (out_h == 144)
switch (currentConfig.filter) { 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; 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; break;
} }
else else
switch (currentConfig.filter) { 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; 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; 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; 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; break;
} }
Pico.est.DrawLineDest = dest; Pico.est.DrawLineDest = (u16 *)dest32 - out_x;
return 0; return 0;
} }
@ -382,6 +403,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_size(screen_w, screen_h);
plat_video_set_buffer(g_screen_ptr); 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 // clear whole screen in all buffers
if (!is_16bit_mode()) if (!is_16bit_mode())
memset32(Pico.est.Draw2FB, 0xe0e0e0e0, (320+8) * (8+240+8) / 4); memset32(Pico.est.Draw2FB, 0xe0e0e0e0, (320+8) * (8+240+8) / 4);
@ -398,6 +427,10 @@ void pemu_loop_end(void)
{ {
/* do one more frame for menu bg */ /* do one more frame for menu bg */
pemu_forced_frame(0, 1); pemu_forced_frame(0, 1);
if (ghost_buf) {
free(ghost_buf);
ghost_buf = NULL;
}
} }
void plat_wait_till_us(unsigned int us_to) void plat_wait_till_us(unsigned int us_to)

View file

@ -2,13 +2,16 @@
static const char *men_scaling_opts[] = { "OFF", "software", "hardware", NULL }; 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_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_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 \ #define MENU_OPTIONS_GFX \
mee_enum_h ("Horizontal scaling", MA_OPT_SCALING, currentConfig.scaling, men_scaling_opts, h_scale), \ 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 ("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 ("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 #define MENU_OPTIONS_ADV