Libretro: Make NTSC filters output in 240p (faster & works better with shaders)

This commit is contained in:
Sour 2018-01-11 22:31:07 -05:00
parent 0b7b98f726
commit 4fc1cb107b
8 changed files with 115 additions and 82 deletions

View file

@ -40,7 +40,15 @@ BisqwitNtscFilter::BisqwitNtscFilter(int resDivider)
}
uint32_t* outputBuffer = (uint32_t*)GetOutputBuffer();
DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer + GetOverscan().GetScreenWidth() * 64 / _resDivider / _resDivider * (120 - GetOverscan().Top), (_isOddFrame ? 8 : 0) + 327360);
//Adjust outputbuffer to start at the middle of the picture
if(_keepVerticalRes) {
outputBuffer += GetOverscan().GetScreenWidth() * 8 / _resDivider * (120 - GetOverscan().Top);
} else {
outputBuffer += GetOverscan().GetScreenWidth() * 64 / _resDivider / _resDivider * (120 - GetOverscan().Top);
}
DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer, (_isOddFrame ? 8 : 0) + 327360);
_workDone = true;
}
@ -68,7 +76,11 @@ void BisqwitNtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
FrameInfo BisqwitNtscFilter::GetFrameInfo()
{
OverscanDimensions overscan = GetOverscan();
return{ overscan.GetScreenWidth()*8/_resDivider, overscan.GetScreenHeight()*8/_resDivider, PPU::ScreenWidth, PPU::ScreenHeight, 4 };
if(_keepVerticalRes) {
return { overscan.GetScreenWidth() * 8 / _resDivider, overscan.GetScreenHeight(), PPU::ScreenWidth, PPU::ScreenHeight, 4 };
} else {
return { overscan.GetScreenWidth() * 8 / _resDivider, overscan.GetScreenHeight() * 8 / _resDivider, PPU::ScreenWidth, PPU::ScreenHeight, 4 };
}
}
void BisqwitNtscFilter::OnBeforeApplyFilter()
@ -76,6 +88,8 @@ void BisqwitNtscFilter::OnBeforeApplyFilter()
PictureSettings pictureSettings = EmulationSettings::GetPictureSettings();
NtscFilterSettings ntscSettings = EmulationSettings::GetNtscFilterSettings();
_keepVerticalRes = ntscSettings.KeepVerticalResolution;
const double pi = std::atan(1.0) * 4;
int contrast = (int)((pictureSettings.Contrast + 1.0) * (pictureSettings.Contrast + 1.0) * 167941);
int saturation = (int)((pictureSettings.Saturation + 1.0) * (pictureSettings.Saturation + 1.0) * 144044);
@ -183,7 +197,10 @@ void BisqwitNtscFilter::DecodeFrame(int startRow, int endRow, uint16_t *ppuOutpu
int pixelsPerCycle = 8 / _resDivider;
int phase = startPhase;
int8_t rowSignal[256 * _signalsPerPixel];
uint32_t rowPixelGap = GetOverscan().GetScreenWidth() * pixelsPerCycle * pixelsPerCycle;
uint32_t rowPixelGap = GetOverscan().GetScreenWidth() * pixelsPerCycle;
if(!_keepVerticalRes) {
rowPixelGap *= pixelsPerCycle;
}
uint32_t* orgBuffer = outputBuffer;
@ -199,18 +216,20 @@ void BisqwitNtscFilter::DecodeFrame(int startRow, int endRow, uint16_t *ppuOutpu
outputBuffer += rowPixelGap;
}
//Generate the missing vertical lines
outputBuffer = orgBuffer;
int lastRow = 239 - GetOverscan().Bottom;
bool verticalBlend = EmulationSettings::GetNtscFilterSettings().VerticalBlend;
for(int y = startRow; y <= endRow; y++) {
uint64_t* currentLine = (uint64_t*)outputBuffer;
uint64_t* nextLine = y == lastRow ? currentLine : (uint64_t*)(outputBuffer + rowPixelGap);
uint64_t* buffer = (uint64_t*)(outputBuffer + rowPixelGap / 2);
RecursiveBlend(4 / _resDivider, buffer, currentLine, nextLine, pixelsPerCycle, verticalBlend);
if(!_keepVerticalRes) {
//Generate the missing vertical lines
outputBuffer = orgBuffer;
int lastRow = 239 - GetOverscan().Bottom;
bool verticalBlend = EmulationSettings::GetNtscFilterSettings().VerticalBlend;
for(int y = startRow; y <= endRow; y++) {
uint64_t* currentLine = (uint64_t*)outputBuffer;
uint64_t* nextLine = y == lastRow ? currentLine : (uint64_t*)(outputBuffer + rowPixelGap);
uint64_t* buffer = (uint64_t*)(outputBuffer + rowPixelGap / 2);
outputBuffer += rowPixelGap;
RecursiveBlend(4 / _resDivider, buffer, currentLine, nextLine, pixelsPerCycle, verticalBlend);
outputBuffer += rowPixelGap;
}
}
}

View file

@ -18,6 +18,8 @@ private:
atomic<bool> _stopThread;
atomic<bool> _workDone;
bool _keepVerticalRes = false;
int _resDivider = 1;
bool _isOddFrame = false;
uint16_t *_ppuOutputBuffer = nullptr;

View file

@ -204,6 +204,7 @@ struct NtscFilterSettings
double Bleed = 0;
bool MergeFields = false;
bool VerticalBlend = false;
bool KeepVerticalResolution = false;
double YFilterLength = 0;
double IFilterLength = 0;
@ -1131,7 +1132,7 @@ public:
return _pictureSettings;
}
static void SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, bool verticalBlend)
static void SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, bool verticalBlend, bool keepVerticalResolution)
{
_ntscFilterSettings.Artifacts = artifacts;
_ntscFilterSettings.Bleed = bleed;
@ -1147,6 +1148,7 @@ public:
_ntscFilterSettings.QFilterLength = qFilterLength;
_ntscFilterSettings.VerticalBlend = verticalBlend;
_ntscFilterSettings.KeepVerticalResolution = keepVerticalResolution;
}
static NtscFilterSettings GetNtscFilterSettings()

View file

@ -8,6 +8,7 @@ NtscFilter::NtscFilter()
memset(_basePalette, 0, 64 * 3);
_ntscData = new nes_ntsc_t();
_ntscSetup = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
_ntscBuffer = new uint32_t[NES_NTSC_OUT_WIDTH(256) * 240];
}
FrameInfo NtscFilter::GetFrameInfo()
@ -15,7 +16,12 @@ FrameInfo NtscFilter::GetFrameInfo()
OverscanDimensions overscan = GetOverscan();
int overscanLeft = overscan.Left > 0 ? NES_NTSC_OUT_WIDTH(overscan.Left) : 0;
int overscanRight = overscan.Right > 0 ? NES_NTSC_OUT_WIDTH(overscan.Right) : 0;
return { NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) - overscanLeft - overscanRight, (PPU::ScreenHeight - overscan.Top - overscan.Bottom) * 2, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth), PPU::ScreenHeight * 2, 4 };
if(_keepVerticalRes) {
return { NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) - overscanLeft - overscanRight, (PPU::ScreenHeight - overscan.Top - overscan.Bottom), NES_NTSC_OUT_WIDTH(PPU::ScreenWidth), PPU::ScreenHeight, 4 };
} else {
return { NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) - overscanLeft - overscanRight, (PPU::ScreenHeight - overscan.Top - overscan.Bottom) * 2, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth), PPU::ScreenHeight * 2, 4 };
}
}
void NtscFilter::OnBeforeApplyFilter()
@ -38,6 +44,9 @@ void NtscFilter::OnBeforeApplyFilter()
PictureSettings pictureSettings = EmulationSettings::GetPictureSettings();
NtscFilterSettings ntscSettings = EmulationSettings::GetNtscFilterSettings();
_keepVerticalRes = ntscSettings.KeepVerticalResolution;
if(paletteChanged || _ntscSetup.hue != pictureSettings.Hue || _ntscSetup.saturation != pictureSettings.Saturation || _ntscSetup.brightness != pictureSettings.Brightness || _ntscSetup.contrast != pictureSettings.Contrast ||
_ntscSetup.artifacts != ntscSettings.Artifacts || _ntscSetup.bleed != ntscSettings.Bleed || _ntscSetup.fringing != ntscSettings.Fringing || _ntscSetup.gamma != ntscSettings.Gamma ||
(_ntscSetup.merge_fields == 1) != ntscSettings.MergeFields || _ntscSetup.resolution != ntscSettings.Resolution || _ntscSetup.sharpness != ntscSettings.Sharpness) {
@ -67,13 +76,11 @@ void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
static bool oddFrame = false;
oddFrame = !oddFrame;
uint32_t* ntscBuffer = new uint32_t[NES_NTSC_OUT_WIDTH(256) * 240];
nes_ntsc_blit(_ntscData, ppuOutputBuffer, PPU::ScreenWidth, oddFrame ? 0 : 1, PPU::ScreenWidth, 240, ntscBuffer, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4);
DoubleOutputHeight(ntscBuffer);
delete[] ntscBuffer;
nes_ntsc_blit(_ntscData, ppuOutputBuffer, PPU::ScreenWidth, oddFrame ? 0 : 1, PPU::ScreenWidth, 240, _ntscBuffer, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4);
GenerateArgbFrame(_ntscBuffer);
}
void NtscFilter::DoubleOutputHeight(uint32_t *ntscBuffer)
void NtscFilter::GenerateArgbFrame(uint32_t *ntscBuffer)
{
uint32_t* outputBuffer = (uint32_t*)GetOutputBuffer();
OverscanDimensions overscan = GetOverscan();
@ -82,43 +89,51 @@ void NtscFilter::DoubleOutputHeight(uint32_t *ntscBuffer)
int rowWidth = NES_NTSC_OUT_WIDTH(PPU::ScreenWidth);
int rowWidthOverscan = rowWidth - overscanLeft - overscanRight;
double scanlineIntensity = 1.0 - EmulationSettings::GetPictureSettings().ScanlineIntensity;
bool verticalBlend = EmulationSettings::GetNtscFilterSettings().VerticalBlend;
if(_keepVerticalRes) {
for(uint32_t y = overscan.Top; y < PPU::ScreenHeight - overscan.Bottom; y++) {
memcpy(outputBuffer, ntscBuffer, rowWidthOverscan * sizeof(uint32_t));
outputBuffer += rowWidthOverscan;
ntscBuffer += rowWidth;
}
} else {
double scanlineIntensity = 1.0 - EmulationSettings::GetPictureSettings().ScanlineIntensity;
bool verticalBlend = EmulationSettings::GetNtscFilterSettings().VerticalBlend;
for(int y = PPU::ScreenHeight - 1 - overscan.Bottom; y >= (int)overscan.Top; y--) {
uint32_t const* in = ntscBuffer + y * rowWidth;
uint32_t* out = outputBuffer + (y - overscan.Top) * 2 * rowWidthOverscan;
for(int y = PPU::ScreenHeight - 1 - overscan.Bottom; y >= (int)overscan.Top; y--) {
uint32_t const* in = ntscBuffer + y * rowWidth;
uint32_t* out = outputBuffer + (y - overscan.Top) * 2 * rowWidthOverscan;
if(verticalBlend || scanlineIntensity < 1.0) {
for(int x = 0; x < rowWidthOverscan; x++) {
uint32_t prev = in[overscanLeft];
uint32_t next = y < 239 ? in[overscanLeft + rowWidth] : 0;
if(verticalBlend || scanlineIntensity < 1.0) {
for(int x = 0; x < rowWidthOverscan; x++) {
uint32_t prev = in[overscanLeft];
uint32_t next = y < 239 ? in[overscanLeft + rowWidth] : 0;
*out = 0xFF000000 | prev;
*out = 0xFF000000 | prev;
/* mix 24-bit rgb without losing low bits */
uint32_t mixed;
if(verticalBlend) {
mixed = (prev + next + ((prev ^ next) & 0x030303)) >> 1;
} else {
mixed = prev;
/* mix 24-bit rgb without losing low bits */
uint32_t mixed;
if(verticalBlend) {
mixed = (prev + next + ((prev ^ next) & 0x030303)) >> 1;
} else {
mixed = prev;
}
if(scanlineIntensity < 1.0) {
uint8_t r = (mixed >> 16) & 0xFF, g = (mixed >> 8) & 0xFF, b = mixed & 0xFF;
r = (uint8_t)(r * scanlineIntensity);
g = (uint8_t)(g * scanlineIntensity);
b = (uint8_t)(b * scanlineIntensity);
*(out + rowWidthOverscan) = 0xFF000000 | (r << 16) | (g << 8) | b;
} else {
*(out + rowWidthOverscan) = 0xFF000000 | mixed;
}
in++;
out++;
}
if(scanlineIntensity < 1.0) {
uint8_t r = (mixed >> 16) & 0xFF, g = (mixed >> 8) & 0xFF, b = mixed & 0xFF;
r = (uint8_t)(r * scanlineIntensity);
g = (uint8_t)(g * scanlineIntensity);
b = (uint8_t)(b * scanlineIntensity);
*(out + rowWidthOverscan) = 0xFF000000 | (r << 16) | (g << 8) | b;
} else {
*(out + rowWidthOverscan) = 0xFF000000 | mixed;
}
in++;
out++;
} else {
memcpy(out, in + overscanLeft, rowWidthOverscan * sizeof(uint32_t));
memcpy(out + rowWidthOverscan, in + overscanLeft, rowWidthOverscan * sizeof(uint32_t));
}
} else {
memcpy(out, in+overscanLeft, rowWidthOverscan * sizeof(uint32_t));
memcpy(out + rowWidthOverscan, in + overscanLeft, rowWidthOverscan * sizeof(uint32_t));
}
}
}
@ -126,4 +141,5 @@ void NtscFilter::DoubleOutputHeight(uint32_t *ntscBuffer)
NtscFilter::~NtscFilter()
{
delete _ntscData;
delete[] _ntscBuffer;
}

View file

@ -8,9 +8,11 @@ class NtscFilter : public BaseVideoFilter
private:
nes_ntsc_setup_t _ntscSetup;
nes_ntsc_t* _ntscData;
bool _keepVerticalRes = false;
uint8_t _basePalette[64 * 3];
uint32_t* _ntscBuffer;
void DoubleOutputHeight(uint32_t *outputBuffer);
void GenerateArgbFrame(uint32_t *outputBuffer);
protected:
void OnBeforeApplyFilter();

View file

@ -464,7 +464,7 @@ namespace InteropEmu {
DllExport void __stdcall GetRgbPalette(uint32_t *paletteBuffer) { EmulationSettings::GetRgbPalette(paletteBuffer); }
DllExport void __stdcall SetRgbPalette(uint32_t *paletteBuffer) { EmulationSettings::SetRgbPalette(paletteBuffer); }
DllExport void __stdcall SetPictureSettings(double brightness, double contrast, double saturation, double hue, double scanlineIntensity) { EmulationSettings::SetPictureSettings(brightness, contrast, saturation, hue, scanlineIntensity); }
DllExport void __stdcall SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, bool verticalBlend) { EmulationSettings::SetNtscFilterSettings(artifacts, bleed, fringing, gamma, resolution, sharpness, mergeFields, yFilterLength, iFilterLength, qFilterLength, verticalBlend); }
DllExport void __stdcall SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, bool verticalBlend) { EmulationSettings::SetNtscFilterSettings(artifacts, bleed, fringing, gamma, resolution, sharpness, mergeFields, yFilterLength, iFilterLength, qFilterLength, verticalBlend, false); }
DllExport void __stdcall SetInputDisplaySettings(uint8_t visiblePorts, InputDisplayPosition displayPosition, bool displayHorizontally) { EmulationSettings::SetInputDisplaySettings(visiblePorts, displayPosition, displayHorizontally); }
DllExport void __stdcall SetAutoSaveOptions(uint32_t delayInMinutes, bool showMessage) { EmulationSettings::SetAutoSaveOptions(delayInMinutes, showMessage); }

View file

@ -33,7 +33,7 @@ public:
if(!_skipMode && _sendFrame) {
//Use Blargg's NTSC filter's max size as a minimum resolution, to prevent changing resolution too often
int32_t newWidth = std::max<int32_t>(width, NES_NTSC_OUT_WIDTH(256));
int32_t newHeight = std::max<int32_t>(height, 480);
int32_t newHeight = std::max<int32_t>(height, 240);
if(_previousWidth != newWidth || _previousHeight != newHeight) {
//Resolution change is needed
retro_system_av_info avInfo = {};

View file

@ -54,7 +54,6 @@ static std::unique_ptr<LibretroKeyManager> _keyManager;
static std::unique_ptr<LibretroMessageManager> _messageManager;
static const char* MesenNtscFilter = "mesen_ntsc_filter";
static const char* MesenNtscVerticalBlend = "mesen_ntsc_vertical_blend";
static const char* MesenPalette = "mesen_palette";
static const char* MesenNoSpriteLimit = "mesen_nospritelimit";
static const char* MesenOverclock = "mesen_overclock";
@ -139,7 +138,6 @@ extern "C" {
static const struct retro_variable vars[] = {
{ MesenNtscFilter, "NTSC filter; Disabled|Composite (Blargg)|S-Video (Blargg)|RGB (Blargg)|Monochrome (Blargg)|Bisqwit 2x|Bisqwit 4x|Bisqwit 8x" },
{ MesenNtscVerticalBlend, "NTSC filter: Vertical blending; disabled|enabled" },
{ MesenPalette, "Palette; Default|Composite Direct (by FirebrandX)|Nes Classic|Nestopia (RGB)|Original Hardware (by FirebrandX)|PVM Style (by FirebrandX)|Sony CXA2025AS|Unsaturated v6 (by FirebrandX)|YUV v3 (by FirebrandX)|Custom" },
{ MesenOverclock, "Overclock; None|Low|Medium|High|Very High" },
{ MesenOverclockType, "Overclock Type; Before NMI (Recommended)|After NMI" },
@ -320,14 +318,6 @@ extern "C" {
}
}
bool verticalBlend = false;
var.key = MesenNtscVerticalBlend;
if(retroEnv(RETRO_ENVIRONMENT_GET_VARIABLE, &var)) {
if(string(var.value) == "enabled") {
verticalBlend = true;
}
}
var.key = MesenNtscFilter;
if(retroEnv(RETRO_ENVIRONMENT_GET_VARIABLE, &var)) {
string value = string(var.value);
@ -335,27 +325,27 @@ extern "C" {
EmulationSettings::SetVideoFilterType(VideoFilterType::None);
} else if(value == "Composite (Blargg)") {
EmulationSettings::SetVideoFilterType(VideoFilterType::NTSC);
EmulationSettings::SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, verticalBlend);
EmulationSettings::SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, false, true);
} else if(value == "S-Video (Blargg)") {
EmulationSettings::SetVideoFilterType(VideoFilterType::NTSC);
EmulationSettings::SetNtscFilterSettings(-1.0, 0, -1.0, 0, 0.2, 0.2, false, 0, 0, 0, verticalBlend);
EmulationSettings::SetNtscFilterSettings(-1.0, 0, -1.0, 0, 0.2, 0.2, false, 0, 0, 0, false, true);
} else if(value == "RGB (Blargg)") {
EmulationSettings::SetVideoFilterType(VideoFilterType::NTSC);
EmulationSettings::SetPictureSettings(0, 0, 0, 0, 0);
EmulationSettings::SetNtscFilterSettings(-1.0, -1.0, -1.0, 0, 0.7, 0.2, false, 0, 0, 0, verticalBlend);
EmulationSettings::SetNtscFilterSettings(-1.0, -1.0, -1.0, 0, 0.7, 0.2, false, 0, 0, 0, false, true);
} else if(value == "Monochrome (Blargg)") {
EmulationSettings::SetVideoFilterType(VideoFilterType::NTSC);
EmulationSettings::SetPictureSettings(0, 0, -1.0, 0, 0);
EmulationSettings::SetNtscFilterSettings(-0.2, -0.1, -0.2, 0, 0.7, 0.2, false, 0, 0, 0, verticalBlend);
EmulationSettings::SetNtscFilterSettings(-0.2, -0.1, -0.2, 0, 0.7, 0.2, false, 0, 0, 0, false, true);
} else if(value == "Bisqwit 2x") {
EmulationSettings::SetVideoFilterType(VideoFilterType::BisqwitNtscQuarterRes);
EmulationSettings::SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, verticalBlend);
EmulationSettings::SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, false, true);
} else if(value == "Bisqwit 4x") {
EmulationSettings::SetVideoFilterType(VideoFilterType::BisqwitNtscHalfRes);
EmulationSettings::SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, verticalBlend);
EmulationSettings::SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, false, true);
} else if(value == "Bisqwit 8x") {
EmulationSettings::SetVideoFilterType(VideoFilterType::BisqwitNtsc);
EmulationSettings::SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, verticalBlend);
EmulationSettings::SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, false, true);
}
}
@ -974,24 +964,26 @@ extern "C" {
RETRO_API void retro_get_system_av_info(struct retro_system_av_info *info)
{
uint32_t scale = 1;
uint32_t hscale = 1;
uint32_t vscale = 1;
switch(EmulationSettings::GetVideoFilterType()) {
case VideoFilterType::NTSC: scale = 2; break;
case VideoFilterType::BisqwitNtscQuarterRes: scale = 2; break;
case VideoFilterType::BisqwitNtscHalfRes: scale = 4; break;
case VideoFilterType::BisqwitNtsc: scale = 8; break;
default: scale = 1; break;
case VideoFilterType::NTSC: hscale = 2; break;
case VideoFilterType::BisqwitNtscQuarterRes: hscale = 2; break;
case VideoFilterType::BisqwitNtscHalfRes: hscale = 4; break;
case VideoFilterType::BisqwitNtsc: hscale = 8; break;
default: hscale = 1; break;
}
HdPackData* hdData = Console::GetHdData();
if(hdData != nullptr) {
scale = hdData->Scale;
hscale = hdData->Scale;
vscale = hdData->Scale;
}
if(scale <= 2) {
_renderer->GetSystemAudioVideoInfo(*info, NES_NTSC_OUT_WIDTH(256), 240 * 2);
if(hscale <= 2) {
_renderer->GetSystemAudioVideoInfo(*info, NES_NTSC_OUT_WIDTH(256), 240 * vscale);
} else {
_renderer->GetSystemAudioVideoInfo(*info, 256 * scale, 240 * scale);
_renderer->GetSystemAudioVideoInfo(*info, 256 * hscale, 240 * vscale);
}
}