Fix colour emphasis support for NTSC filter & raw palette + nes decoder shader

- this implementation is based on a more accurate colour emphasis from fceux.

- The raw palette + nes decoder shader was kinda incomplete since the implementation was based on
a per-frame basis which means that the emphasis bits were read once and applied to the whole frame. This
means that some games that uses a per pixel or per scanline emphasis would not appear correct. The more
accurate implementation reads emphasis bits from bits 5-7 of PPU[1] and saves this info in a separate frame.

- The same implementation is also used to fix emphasis for the ntsc filter.
This commit is contained in:
negativeExponent
2020-02-25 11:51:41 +08:00
parent 4f599a77f9
commit fa74a364de
7 changed files with 72 additions and 58 deletions

View File

@@ -277,7 +277,6 @@ extern int ipalette;
/* table for currently loaded palette */
static uint8_t base_palette[192];
static unsigned palette_changed;
struct st_palettes {
char name[32];
@@ -629,16 +628,14 @@ static void NTSCFilter_Setup(void)
break;
}
if (palette_changed)
{
if ((GameInfo->type != GIT_VSUNI) && (current_palette == PAL_DEFAULT || current_palette == PAL_RAW))
/* use ntsc default palette instead of internal default palette for that "identity" effect */
ntsc_setup.base_palette = NULL;
else
/* use internal palette, this includes palette presets, external palette and custom palettes
ntsc_setup.merge_fields = 0;
if ((GameInfo->type != GIT_VSUNI) && (current_palette == PAL_DEFAULT || current_palette == PAL_RAW))
/* use ntsc default palette instead of internal default palette for that "identity" effect */
ntsc_setup.base_palette = NULL;
else
/* use internal palette, this includes palette presets, external palette and custom palettes
* for VS. System games */
ntsc_setup.base_palette = (unsigned char const*)palo;
}
ntsc_setup.base_palette = (unsigned char const *)palo;
nes_ntsc_init(&nes_ntsc, &ntsc_setup);
}
@@ -977,7 +974,6 @@ static void retro_set_custom_palette(void)
ipalette = 0;
use_raw_palette = false;
palette_changed = 0;
if ((current_palette == PAL_DEFAULT) ||
(current_palette == PAL_CUSTOM) ||
@@ -994,7 +990,6 @@ static void retro_set_custom_palette(void)
* else it will load default NES palette.
* VS Unisystem should always use default palette.
*/
palette_changed = 1;
}
/* setup raw palette */
@@ -1023,7 +1018,6 @@ static void retro_set_custom_palette(void)
base_palette[ i * 3 + 2 ] = ( data >> 0 ) & 0xff; /* blue */
}
FCEUI_SetPaletteArray( base_palette );
palette_changed = 1;
}
#if defined(RENDER_GSKIT_PS2)
@@ -1841,11 +1835,12 @@ static void retro_run_blit(uint8_t *gfx)
#ifdef HAVE_NTSC_FILTER
if (use_ntsc)
{
int y;
uint8_t *deemp;
unsigned out_width, out_height, out_pitch;
int y;
/*burst_phase ^= 1;
if (ntsc_setup.merge_fields) burst_phase = 0;*/
burst_phase ^= 1;
if (ntsc_setup.merge_fields) burst_phase = 0;
width = overscan_h ? 240 : 256;
height = overscan_v ? 224 : 240;
@@ -1853,33 +1848,35 @@ static void retro_run_blit(uint8_t *gfx)
out_height = height;
out_pitch = out_width * sizeof(uint16_t);
gfx += (overscan_v ? ((overscan_h ? 8 : 0) + 256 * 8) : (overscan_h ? 8 : 0));
deemp = XDBuf + (gfx - XBuf); /* get colour emphasis */
nes_ntsc_blit(&nes_ntsc, (const unsigned char*)gfx, NES_WIDTH, burst_phase, width, height, ntsc_video_out, out_pitch);
nes_ntsc_blit(&nes_ntsc, (unsigned char const*)gfx, (unsigned char const*)deemp, NES_WIDTH, burst_phase, width, height, ntsc_video_out, out_pitch);
#if 0
/* this doubles the height for proper scaling, disabled for performance reasons */
* NOTE: buffer height needs to be doubled as well */
out_height <<= 1;
for (y = out_height / 2; --y >= 0;)
if (!retain_height)
{
uint8_t const *in = ntsc_video_out + y * out_pitch;
uint8_t *out = ntsc_video_out + y * 2 * out_pitch;
int n;
for (n = out_width; n; --n)
/* this doubles the height for proper scaling, disabled for performance reasons.
* NOTE: buffer height needs to be doubled as well */
out_height <<= 1;
for (y = out_height / 2; --y >= 0;)
{
unsigned prev = *(uint16_t*)in;
unsigned next = *(uint16_t*)(in + out_pitch);
/* mix 16-bit rgb without losing low bits */
unsigned mixed = prev + next + ((prev ^ next) & 0x0821);
/* darken by 12% */
*(uint16_t*)out = prev;
*(uint16_t*)(out + out_pitch) = (mixed >> 1) - (mixed >> 4 & 0x18E3);
in += 2;
out += 2;
uint8_t const *in = ntsc_video_out + y * out_pitch;
uint8_t *out = ntsc_video_out + y * 2 * out_pitch;
int n;
for (n = out_width; n; --n)
{
unsigned prev = *(uint16_t *)in;
unsigned next = *(uint16_t *)(in + out_pitch);
/* mix 16-bit rgb without losing low bits */
unsigned mixed = prev + next + ((prev ^ next) & 0x0821);
/* darken by 12% */
*(uint16_t *)out = prev;
*(uint16_t *)(out + out_pitch) = (mixed >> 1) - (mixed >> 4 & 0x18E3);
in += 2;
out += 2;
}
}
}
#endif
video_cb(ntsc_video_out, out_width, out_height, out_pitch);
}
else
@@ -1893,11 +1890,10 @@ static void retro_run_blit(uint8_t *gfx)
if (use_raw_palette)
{
extern uint8 PPU[4];
int deemp = (PPU[1] >> 5) << 2;
for (y = 0; y < height; y++, gfx += incr)
for (x = 0; x < width; x++, gfx++)
fceu_video_out[y * width + x] = retro_palette[*gfx & 0x3F] | deemp;
uint8_t *deemp = XDBuf + (gfx - XBuf);
for (y = 0; y < height; y++, gfx += incr, deemp += incr)
for (x = 0; x < width; x++, gfx++, deemp++)
fceu_video_out[y * width + x] = retro_palette[*gfx & 0x3F] | (*deemp << 2);
}
else
{
@@ -2390,7 +2386,6 @@ bool retro_load_game(const struct retro_game_info *game)
systemRegion = (dendy << 1) | (retro_get_region() & 1);
current_palette = 0;
palette_changed = 0;
ResetPalette();
FCEUD_SoundToggle();

View File

@@ -233,15 +233,16 @@ void nes_ntsc_init( nes_ntsc_t* ntsc, nes_ntsc_setup_t const* setup )
#ifndef NES_NTSC_NO_BLITTERS
void nes_ntsc_blit( nes_ntsc_t const* ntsc, NES_NTSC_IN_T const* input, long in_row_width,
void nes_ntsc_blit( nes_ntsc_t const* ntsc, NES_NTSC_IN_T const* input, NES_NTSC_IN_T const* inputD, long in_row_width,
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 1) / nes_ntsc_in_chunk;
for ( ; in_height; --in_height )
{
NES_NTSC_IN_T const* line_in = input;
NES_NTSC_IN_T const* line_inD = inputD;
NES_NTSC_BEGIN_ROW( ntsc, burst_phase,
nes_ntsc_black, nes_ntsc_black, NES_NTSC_ADJ_IN( *line_in ) );
nes_ntsc_black, nes_ntsc_black, NES_NTSC_ADJ_IN( *line_in, *line_inD ) );
nes_ntsc_out_t* restrict line_out = (nes_ntsc_out_t*) rgb_out;
int n;
++line_in;
@@ -249,20 +250,21 @@ void nes_ntsc_blit( nes_ntsc_t const* ntsc, NES_NTSC_IN_T const* input, long in_
for ( n = chunk_count; n; --n )
{
/* order of input and output pixels must not be altered */
NES_NTSC_COLOR_IN( 0, NES_NTSC_ADJ_IN( line_in [0] ) );
NES_NTSC_COLOR_IN( 0, NES_NTSC_ADJ_IN( line_in [0], line_inD[0] ) );
NES_NTSC_RGB_OUT( 0, line_out [0], NES_NTSC_OUT_DEPTH );
NES_NTSC_RGB_OUT( 1, line_out [1], NES_NTSC_OUT_DEPTH );
NES_NTSC_COLOR_IN( 1, NES_NTSC_ADJ_IN( line_in [1] ) );
NES_NTSC_COLOR_IN( 1, NES_NTSC_ADJ_IN( line_in [1], line_inD[1] ) );
NES_NTSC_RGB_OUT( 2, line_out [2], NES_NTSC_OUT_DEPTH );
NES_NTSC_RGB_OUT( 3, line_out [3], NES_NTSC_OUT_DEPTH );
NES_NTSC_COLOR_IN( 2, NES_NTSC_ADJ_IN( line_in [2] ) );
NES_NTSC_COLOR_IN( 2, NES_NTSC_ADJ_IN( line_in [2], line_inD[2] ) );
NES_NTSC_RGB_OUT( 4, line_out [4], NES_NTSC_OUT_DEPTH );
NES_NTSC_RGB_OUT( 5, line_out [5], NES_NTSC_OUT_DEPTH );
NES_NTSC_RGB_OUT( 6, line_out [6], NES_NTSC_OUT_DEPTH );
line_in += 3;
line_inD += 3;
line_out += 7;
}
@@ -281,7 +283,8 @@ void nes_ntsc_blit( nes_ntsc_t const* ntsc, NES_NTSC_IN_T const* input, long in_
NES_NTSC_RGB_OUT( 6, line_out [6], NES_NTSC_OUT_DEPTH );
burst_phase = (burst_phase + 1) % nes_ntsc_burst_count;
input += in_row_width;
input += in_row_width;
inputD += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}

View File

@@ -61,8 +61,8 @@ In_row_width is the number of pixels to get to the next input row. Out_pitch
is the number of *bytes* to get to the next output row. Output pixel format
is set by NES_NTSC_OUT_DEPTH (defaults to 16-bit RGB). */
void nes_ntsc_blit( nes_ntsc_t const* ntsc, NES_NTSC_IN_T const* nes_in,
long in_row_width, int burst_phase, int in_width, int in_height,
void* rgb_out, long out_pitch );
NES_NTSC_IN_T const* nes_inD, long in_row_width, int burst_phase, int in_width,
int in_height, void* rgb_out, long out_pitch );
/* Number of output pixels written by blitter for given input width. Width might
be rounded down slightly; use NES_NTSC_IN_WIDTH() on result to find rounded

View File

@@ -19,7 +19,7 @@ if you enable emphasis above. */
/* Each raw pixel input value is passed through this. You might want to mask
the pixel index if you use the high bits as flags, etc. */
#define NES_NTSC_ADJ_IN( in ) (in & 0x3F)
#define NES_NTSC_ADJ_IN( in, deemp ) ( in & 0x3F ) | ( deemp << 6 )
/* For each pixel, this is the basic operation:
output_color = color_palette [NES_NTSC_ADJ_IN( NES_NTSC_IN_T )] */

View File

@@ -656,10 +656,12 @@ static void Fixit1(void) {
void MMC5_hb(int); /* Ugh ugh ugh. */
static void DoLine(void)
{
int x;
uint8 *target = NULL;
if (scanline >= 240 && scanline != totalscanlines)
{
int x, colour_emphasis;
uint8 *target = NULL;
uint8 *dtarget = NULL;
if (scanline >= 240 && scanline != totalscanlines)
{
X6502_Run(256 + 69);
scanline++;
X6502_Run(16);
@@ -667,6 +669,7 @@ static void DoLine(void)
}
target = XBuf + ((scanline < 240 ? scanline : 240) << 8);
dtarget = XDBuf + ((scanline < 240 ? scanline : 240) << 8);
if (MMC5Hack && (ScreenON || SpriteON)) MMC5_hb(scanline);
@@ -699,7 +702,12 @@ static void DoLine(void)
for (x = 63; x >= 0; x--)
*(uint32*)&target[x << 2] = ((*(uint32*)&target[x << 2]) & 0x3f3f3f3f) | 0x80808080;
sphitx = 0x100;
//write the actual colour emphasis
colour_emphasis = ((PPU[1] >> 5) << 24) | ((PPU[1] >> 5) << 16) | ((PPU[1] >> 5) << 8) | ((PPU[1] >> 5) << 0);
for (x = 63; x >= 0; x--)
*(uint32*)&dtarget[x << 2] = colour_emphasis;
sphitx = 0x100;
if (ScreenON || SpriteON)
FetchSpriteData();

View File

@@ -36,6 +36,7 @@
#include "vsuni.h"
uint8 *XBuf = NULL;
uint8 *XDBuf = NULL;
int show_crosshair = 0;
void FCEU_KillVirtualVideo(void)
@@ -43,6 +44,9 @@ void FCEU_KillVirtualVideo(void)
if (XBuf)
free(XBuf);
XBuf = 0;
if (XDBuf)
free(XDBuf);
XDBuf = 0;
}
int FCEU_InitVirtualVideo(void)
@@ -50,11 +54,14 @@ int FCEU_InitVirtualVideo(void)
/* 256 bytes per scanline, * 240 scanline maximum, +8 for alignment, */
if (!XBuf)
XBuf = (uint8*)(FCEU_malloc(256 * (256 + extrascanlines + 8)));
if (!XDBuf)
XDBuf = (uint8*)(FCEU_malloc(256 * (256 + extrascanlines + 8)));
if (!XBuf)
if (!XBuf || !XDBuf)
return 0;
memset(XBuf, 128, 256 * (256 + extrascanlines));
memset(XBuf, 128, 256 * (256 + extrascanlines + 8));
memset(XDBuf, 128, 256 * (256 + extrascanlines + 8));
return 1;
}

View File

@@ -5,6 +5,7 @@ int FCEU_InitVirtualVideo(void);
void FCEU_KillVirtualVideo(void);
int SaveSnapshot(void);
extern uint8 *XBuf;
extern uint8 *XDBuf;
extern int show_crosshair;
void FCEU_DrawNumberRow(uint8 *XBuf, int *nstatus, int cur);