HD Packs: Allow non-ntsc video filters to be used on top of a HD pack

This commit is contained in:
Souryo 2017-08-15 23:59:55 -04:00
parent 6ca49dd4af
commit 6691660617
8 changed files with 118 additions and 84 deletions

View file

@ -12,7 +12,6 @@ ScaleFilter::ScaleFilter(ScaleFilterType scaleFilterType, uint32_t scale)
{
_scaleFilterType = scaleFilterType;
_filterScale = scale;
_decodedPpuBuffer = new uint32_t[PPU::PixelCount];
if(!_hqxInitDone && _scaleFilterType == ScaleFilterType::HQX) {
hqxInit();
@ -22,68 +21,72 @@ ScaleFilter::ScaleFilter(ScaleFilterType scaleFilterType, uint32_t scale)
ScaleFilter::~ScaleFilter()
{
delete[] _decodedPpuBuffer;
if(_outputBuffer) {
delete[] _outputBuffer;
}
}
FrameInfo ScaleFilter::GetFrameInfo()
uint32_t ScaleFilter::GetScale()
{
OverscanDimensions overscan = GetOverscan();
return{ overscan.GetScreenWidth()*_filterScale, overscan.GetScreenHeight()*_filterScale, PPU::ScreenWidth*_filterScale, PPU::ScreenHeight*_filterScale, 4 };
return _filterScale;
}
void ScaleFilter::ApplyPrescaleFilter()
void ScaleFilter::ApplyPrescaleFilter(uint32_t *inputArgbBuffer)
{
uint32_t* outputBuffer = (uint32_t*)GetOutputBuffer();
uint32_t* outputBuffer = _outputBuffer;
OverscanDimensions overscan = GetOverscan();
uint32_t height = overscan.GetScreenHeight();
uint32_t width = overscan.GetScreenWidth();
uint32_t *inputBuffer = _decodedPpuBuffer;
for(uint32_t y = 0; y < height; y++) {
for(uint32_t x = 0; x < width; x++) {
for(uint32_t y = 0; y < _height; y++) {
for(uint32_t x = 0; x < _width; x++) {
for(uint32_t i = 0; i < _filterScale; i++) {
*(outputBuffer++) = *inputBuffer;
*(outputBuffer++) = *inputArgbBuffer;
}
inputBuffer++;
inputArgbBuffer++;
}
for(uint32_t i = 1; i< _filterScale; i++) {
memcpy(outputBuffer, outputBuffer - width*_filterScale, width*_filterScale *4);
outputBuffer += width*_filterScale;
for(uint32_t i = 1; i < _filterScale; i++) {
memcpy(outputBuffer, outputBuffer - _width*_filterScale, _width*_filterScale *4);
outputBuffer += _width*_filterScale;
}
}
}
void ScaleFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
void ScaleFilter::UpdateOutputBuffer(uint32_t width, uint32_t height)
{
DecodePpuBuffer(ppuOutputBuffer, _decodedPpuBuffer, false);
if(!_outputBuffer || width != _width || height != _height) {
if(_outputBuffer) {
delete[] _outputBuffer;
}
OverscanDimensions overscan = GetOverscan();
uint32_t height = overscan.GetScreenHeight();
uint32_t width = overscan.GetScreenWidth();
uint32_t* outputBuffer = (uint32_t*)GetOutputBuffer();
_width = width;
_height = height;
_outputBuffer = new uint32_t[_width*_height*_filterScale*_filterScale];
}
}
uint32_t* ScaleFilter::ApplyFilter(uint32_t *inputArgbBuffer, uint32_t width, uint32_t height)
{
UpdateOutputBuffer(width, height);
if(_scaleFilterType == ScaleFilterType::xBRZ) {
xbrz::scale(_filterScale, _decodedPpuBuffer, outputBuffer, width, height, xbrz::ColorFormat::ARGB);
xbrz::scale(_filterScale, inputArgbBuffer, _outputBuffer, width, height, xbrz::ColorFormat::ARGB);
} else if(_scaleFilterType == ScaleFilterType::HQX) {
hqx(_filterScale, _decodedPpuBuffer, outputBuffer, width, height);
hqx(_filterScale, inputArgbBuffer, _outputBuffer, width, height);
} else if(_scaleFilterType == ScaleFilterType::Scale2x) {
scale(_filterScale, outputBuffer, width*sizeof(uint32_t)*_filterScale, _decodedPpuBuffer, width*sizeof(uint32_t), 4, width, height);
scale(_filterScale, _outputBuffer, width*sizeof(uint32_t)*_filterScale, inputArgbBuffer, width*sizeof(uint32_t), 4, width, height);
} else if(_scaleFilterType == ScaleFilterType::_2xSai) {
twoxsai_generic_xrgb8888(width, height, _decodedPpuBuffer, width, outputBuffer, width * _filterScale);
twoxsai_generic_xrgb8888(width, height, inputArgbBuffer, width, _outputBuffer, width * _filterScale);
} else if(_scaleFilterType == ScaleFilterType::Super2xSai) {
supertwoxsai_generic_xrgb8888(width, height, _decodedPpuBuffer, width, outputBuffer, width * _filterScale);
supertwoxsai_generic_xrgb8888(width, height, inputArgbBuffer, width, _outputBuffer, width * _filterScale);
} else if(_scaleFilterType == ScaleFilterType::SuperEagle) {
supereagle_generic_xrgb8888(width, height, _decodedPpuBuffer, width, outputBuffer, width * _filterScale);
supereagle_generic_xrgb8888(width, height, inputArgbBuffer, width, _outputBuffer, width * _filterScale);
} else if(_scaleFilterType == ScaleFilterType::Prescale) {
ApplyPrescaleFilter();
ApplyPrescaleFilter(inputArgbBuffer);
}
double scanlineIntensity = 1.0 - EmulationSettings::GetPictureSettings().ScanlineIntensity;
if(scanlineIntensity < 1.0) {
for(int y = 1, yMax = height * _filterScale; y < yMax; y += 2) {
for(int x = 0, xMax = width * _filterScale; x < xMax; x++) {
uint32_t &color = outputBuffer[y*xMax + x];
uint32_t &color = _outputBuffer[y*xMax + x];
uint8_t r = (color >> 16) & 0xFF, g = (color >> 8) & 0xFF, b = color & 0xFF;
r = (uint8_t)(r * scanlineIntensity);
g = (uint8_t)(g * scanlineIntensity);
@ -92,4 +95,6 @@ void ScaleFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
}
}
}
return _outputBuffer;
}

View file

@ -3,20 +3,23 @@
#include "stdafx.h"
#include "DefaultVideoFilter.h"
class ScaleFilter : public DefaultVideoFilter
class ScaleFilter
{
private:
static bool _hqxInitDone;
uint32_t *_decodedPpuBuffer;
uint32_t _filterScale;
ScaleFilterType _scaleFilterType;
uint32_t *_outputBuffer = nullptr;
uint32_t _width = 0;
uint32_t _height = 0;
void ApplyPrescaleFilter();
void ApplyPrescaleFilter(uint32_t *inputArgbBuffer);
void UpdateOutputBuffer(uint32_t width, uint32_t height);
public:
ScaleFilter(ScaleFilterType scaleFilterType, uint32_t scale);
virtual ~ScaleFilter();
~ScaleFilter();
void ApplyFilter(uint16_t *ppuOutputBuffer);
FrameInfo GetFrameInfo();
uint32_t GetScale();
uint32_t* ApplyFilter(uint32_t *inputArgbBuffer, uint32_t width, uint32_t height);
};

View file

@ -65,42 +65,44 @@ void VideoDecoder::GetScreenSize(ScreenSize &size, bool ignoreScale)
void VideoDecoder::UpdateVideoFilter()
{
VideoFilterType newFilter = EmulationSettings::GetVideoFilterType();
if(_hdScreenTiles) {
newFilter = VideoFilterType::HdPack;
}
if(_videoFilterType != newFilter || _videoFilter == nullptr) {
if(_videoFilterType != newFilter || _videoFilter == nullptr || (_hdScreenTiles && !_hdFilterEnabled) || (!_hdScreenTiles && _hdFilterEnabled)) {
_videoFilterType = newFilter;
_videoFilter.reset(new DefaultVideoFilter());
_scaleFilter.reset();
switch(_videoFilterType) {
case VideoFilterType::None: _videoFilter.reset(new DefaultVideoFilter()); break;
case VideoFilterType::NTSC: _videoFilter.reset(new NtscFilter()); break;
case VideoFilterType::BisqwitNtsc: _videoFilter.reset(new BisqwitNtscFilter(1)); break;
case VideoFilterType::BisqwitNtscHalfRes: _videoFilter.reset(new BisqwitNtscFilter(2)); break;
case VideoFilterType::BisqwitNtscQuarterRes: _videoFilter.reset(new BisqwitNtscFilter(4)); break;
case VideoFilterType::xBRZ2x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::xBRZ, 2)); break;
case VideoFilterType::xBRZ3x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::xBRZ, 3)); break;
case VideoFilterType::xBRZ4x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::xBRZ, 4)); break;
case VideoFilterType::xBRZ5x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::xBRZ, 5)); break;
case VideoFilterType::xBRZ6x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::xBRZ, 6)); break;
case VideoFilterType::HQ2x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::HQX, 2)); break;
case VideoFilterType::HQ3x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::HQX, 3)); break;
case VideoFilterType::HQ4x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::HQX, 4)); break;
case VideoFilterType::Scale2x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::Scale2x, 2)); break;
case VideoFilterType::Scale3x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::Scale2x, 3)); break;
case VideoFilterType::Scale4x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::Scale2x, 4)); break;
case VideoFilterType::_2xSai: _videoFilter.reset(new ScaleFilter(ScaleFilterType::_2xSai, 2)); break;
case VideoFilterType::Super2xSai: _videoFilter.reset(new ScaleFilter(ScaleFilterType::Super2xSai, 2)); break;
case VideoFilterType::SuperEagle: _videoFilter.reset(new ScaleFilter(ScaleFilterType::SuperEagle, 2)); break;
case VideoFilterType::xBRZ2x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::xBRZ, 2)); break;
case VideoFilterType::xBRZ3x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::xBRZ, 3)); break;
case VideoFilterType::xBRZ4x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::xBRZ, 4)); break;
case VideoFilterType::xBRZ5x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::xBRZ, 5)); break;
case VideoFilterType::xBRZ6x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::xBRZ, 6)); break;
case VideoFilterType::HQ2x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::HQX, 2)); break;
case VideoFilterType::HQ3x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::HQX, 3)); break;
case VideoFilterType::HQ4x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::HQX, 4)); break;
case VideoFilterType::Scale2x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::Scale2x, 2)); break;
case VideoFilterType::Scale3x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::Scale2x, 3)); break;
case VideoFilterType::Scale4x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::Scale2x, 4)); break;
case VideoFilterType::_2xSai: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::_2xSai, 2)); break;
case VideoFilterType::Super2xSai: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::Super2xSai, 2)); break;
case VideoFilterType::SuperEagle: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::SuperEagle, 2)); break;
case VideoFilterType::Prescale2x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 2)); break;
case VideoFilterType::Prescale3x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 3)); break;
case VideoFilterType::Prescale4x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 4)); break;
case VideoFilterType::Prescale6x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 6)); break;
case VideoFilterType::Prescale8x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 8)); break;
case VideoFilterType::Prescale10x: _videoFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 10)); break;
case VideoFilterType::Prescale2x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 2)); break;
case VideoFilterType::Prescale3x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 3)); break;
case VideoFilterType::Prescale4x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 4)); break;
case VideoFilterType::Prescale6x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 6)); break;
case VideoFilterType::Prescale8x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 8)); break;
case VideoFilterType::Prescale10x: _scaleFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 10)); break;
}
case VideoFilterType::HdPack: _videoFilter.reset(new HdVideoFilter()); break;
_hdFilterEnabled = false;
if(_hdScreenTiles) {
_videoFilter.reset(new HdVideoFilter());
_hdFilterEnabled = true;
}
}
}
@ -109,11 +111,16 @@ void VideoDecoder::DecodeFrame()
{
UpdateVideoFilter();
if(_videoFilterType == VideoFilterType::HdPack) {
if(_hdFilterEnabled) {
((HdVideoFilter*)_videoFilter.get())->SetHdScreenTiles(_hdScreenTiles);
}
_videoFilter->SendFrame(_ppuOutputBuffer);
uint32_t* outputBuffer = (uint32_t*)_videoFilter->GetOutputBuffer();
if(_scaleFilter) {
outputBuffer = _scaleFilter->ApplyFilter(outputBuffer, _videoFilter->GetFrameInfo().Width, _videoFilter->GetFrameInfo().Height);
}
ScreenSize screenSize;
GetScreenSize(screenSize, true);
if(_previousScale != EmulationSettings::GetVideoScale() || screenSize.Height != _previousScreenSize.Height || screenSize.Width != _previousScreenSize.Width) {
@ -123,11 +130,17 @@ void VideoDecoder::DecodeFrame()
_previousScreenSize = screenSize;
FrameInfo frameInfo = _videoFilter->GetFrameInfo();
if(_scaleFilter) {
frameInfo.Height *= _scaleFilter->GetScale();
frameInfo.Width *= _scaleFilter->GetScale();
frameInfo.OriginalHeight *= _scaleFilter->GetScale();
frameInfo.OriginalWidth *= _scaleFilter->GetScale();
}
_frameChanged = false;
//Rewind manager will take care of sending the correct frame to the video renderer
RewindManager::SendFrame(_videoFilter->GetOutputBuffer(), frameInfo.Width, frameInfo.Height);
RewindManager::SendFrame(outputBuffer, frameInfo.Width, frameInfo.Height);
}
void VideoDecoder::DebugDecodeFrame(uint16_t* inputBuffer, uint32_t* outputBuffer, uint32_t length)

View file

@ -9,6 +9,7 @@ using std::thread;
#include "FrameInfo.h"
class BaseVideoFilter;
class ScaleFilter;
class IRenderingDevice;
struct HdPpuPixelInfo;
@ -26,6 +27,7 @@ private:
uint16_t *_ppuOutputBuffer = nullptr;
HdPpuPixelInfo *_hdScreenTiles = nullptr;
bool _hdFilterEnabled = false;
unique_ptr<thread> _decodeThread;
@ -40,6 +42,7 @@ private:
VideoFilterType _videoFilterType = VideoFilterType::None;
unique_ptr<BaseVideoFilter> _videoFilter;
unique_ptr<ScaleFilter> _scaleFilter;
void UpdateVideoFilter();

View file

@ -185,6 +185,8 @@ namespace Mesen.GUI.Forms
this.mnuMemoryViewer = new System.Windows.Forms.ToolStripMenuItem();
this.mnuPpuViewer = new System.Windows.Forms.ToolStripMenuItem();
this.mnuTraceLogger = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem25 = new System.Windows.Forms.ToolStripSeparator();
this.mnuEditHeader = new System.Windows.Forms.ToolStripMenuItem();
this.mnuHelp = new System.Windows.Forms.ToolStripMenuItem();
this.mnuCheckForUpdates = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem20 = new System.Windows.Forms.ToolStripSeparator();
@ -192,8 +194,6 @@ namespace Mesen.GUI.Forms
this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator();
this.mnuHelpWindow = new System.Windows.Forms.ToolStripMenuItem();
this.mnuAbout = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem25 = new System.Windows.Forms.ToolStripSeparator();
this.mnuEditHeader = new System.Windows.Forms.ToolStripMenuItem();
this.panelRenderer.SuspendLayout();
this.panelInfo.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.picIcon)).BeginInit();
@ -307,7 +307,7 @@ namespace Mesen.GUI.Forms
this.mnuGame,
this.mnuOptions,
this.mnuTools,
this.mnuDebug,
this.mnuDebug,
this.mnuHelp});
this.menuStrip.Location = new System.Drawing.Point(0, 0);
this.menuStrip.Name = "menuStrip";
@ -882,7 +882,7 @@ namespace Mesen.GUI.Forms
//
this.mnuScale4xFilter.Name = "mnuScale4xFilter";
this.mnuScale4xFilter.Size = new System.Drawing.Size(206, 22);
this.mnuScale4xFilter.Text = "Scale 4x";
this.mnuScale4xFilter.Text = "Scale4x";
this.mnuScale4xFilter.Click += new System.EventHandler(this.mnuScale4xFilter_Click);
//
// toolStripMenuItem23
@ -1500,6 +1500,19 @@ namespace Mesen.GUI.Forms
this.mnuTraceLogger.Text = "Trace Logger";
this.mnuTraceLogger.Click += new System.EventHandler(this.mnuTraceLogger_Click);
//
// toolStripMenuItem25
//
this.toolStripMenuItem25.Name = "toolStripMenuItem25";
this.toolStripMenuItem25.Size = new System.Drawing.Size(193, 6);
//
// mnuEditHeader
//
this.mnuEditHeader.Image = global::Mesen.GUI.Properties.Resources.Edit;
this.mnuEditHeader.Name = "mnuEditHeader";
this.mnuEditHeader.Size = new System.Drawing.Size(196, 22);
this.mnuEditHeader.Text = "Edit iNES Header";
this.mnuEditHeader.Click += new System.EventHandler(this.mnuEditHeader_Click);
//
// mnuHelp
//
this.mnuHelp.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
@ -1554,19 +1567,6 @@ namespace Mesen.GUI.Forms
this.mnuAbout.Text = "About";
this.mnuAbout.Click += new System.EventHandler(this.mnuAbout_Click);
//
// toolStripMenuItem25
//
this.toolStripMenuItem25.Name = "toolStripMenuItem25";
this.toolStripMenuItem25.Size = new System.Drawing.Size(193, 6);
//
// mnuEditHeader
//
this.mnuEditHeader.Image = global::Mesen.GUI.Properties.Resources.Edit;
this.mnuEditHeader.Name = "mnuEditHeader";
this.mnuEditHeader.Size = new System.Drawing.Size(196, 22);
this.mnuEditHeader.Text = "Edit iNES Header";
this.mnuEditHeader.Click += new System.EventHandler(this.mnuEditHeader_Click);
//
// frmMain
//
this.AllowDrop = true;

View file

@ -611,6 +611,12 @@ namespace Mesen.GUI.Forms
mnuSelectDisk.Enabled = autoInsertDisabled;
mnuEjectDisk.Enabled = autoInsertDisabled;
mnuSwitchDiskSide.Enabled = autoInsertDisabled;
bool isHdPackLoader = InteropEmu.IsHdPackLoaded();
mnuNtscFilter.Enabled = !isHdPackLoader;
mnuNtscBisqwitQuarterFilter.Enabled = !isHdPackLoader;
mnuNtscBisqwitHalfFilter.Enabled = !isHdPackLoader;
mnuNtscBisqwitFullFilter.Enabled = !isHdPackLoader;
}
} catch { }
}

View file

@ -533,6 +533,8 @@ namespace Mesen.GUI
yScroll = (int)(ppuScroll >> 16) & 0xFFFF;
}
[DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsHdPackLoaded();
[DllImport(DLLPath)] public static extern void HdBuilderStartRecording(
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string saveFolder,
ScaleFilterType filterType,

View file

@ -475,6 +475,8 @@ namespace InteropEmu {
}
}
DllExport bool __stdcall IsHdPackLoaded() { return Console::GetHdData() != nullptr; }
DllExport void __stdcall HdBuilderStartRecording(char* saveFolder, ScaleFilterType filterType, uint32_t scale, uint32_t flags, uint32_t chrRamBankSize) { Console::StartRecordingHdPack(saveFolder, filterType, scale, flags, chrRamBankSize); }
DllExport void __stdcall HdBuilderStopRecording() { Console::StopRecordingHdPack(); }