mirror of
https://github.com/RaySollium99/picodrive.git
synced 2025-09-05 15:27:46 -04:00
sms, add ghosting for GG LCD (generic+libretro only)
This commit is contained in:
parent
27005bdb5f
commit
f55ce7bf47
7 changed files with 135 additions and 18 deletions
|
@ -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;
|
||||
|
|
|
@ -89,6 +89,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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -1913,6 +1947,10 @@ 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++) {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
if (notice)
|
||||
emu_osd_text16(4, g_screen_height - 8, notice);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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_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);
|
||||
|
@ -398,6 +427,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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue