Imported video decode/renderer classes and related utilities from Mesen

This commit is contained in:
Sour 2019-02-13 23:02:43 -05:00
parent 69cf69fa6f
commit aed9270e52
28 changed files with 1609 additions and 58 deletions

View file

@ -1,16 +1,10 @@
#pragma once
#include "../Core/IMessageManager.h"
#include "IMessageManager.h"
#include "SettingTypes.h"
#include "../Utilities/Timer.h"
class Console;
//TODO
enum class VideoResizeFilter
{
NearestNeighbor = 0,
Bilinear = 1
};
class BaseRenderer : public IMessageManager
{
private:

141
Core/BaseVideoFilter.cpp Normal file
View file

@ -0,0 +1,141 @@
#include "stdafx.h"
#include "BaseVideoFilter.h"
#include "MessageManager.h"
#include "../Utilities/PNGHelper.h"
#include "../Utilities/FolderUtilities.h"
#include "Console.h"
BaseVideoFilter::BaseVideoFilter(shared_ptr<Console> console)
{
_console = console;
_overscan = {}; //TODO _console->GetSettings()->GetOverscanDimensions();
}
BaseVideoFilter::~BaseVideoFilter()
{
auto lock = _frameLock.AcquireSafe();
if(_outputBuffer) {
delete[] _outputBuffer;
_outputBuffer = nullptr;
}
}
void BaseVideoFilter::UpdateBufferSize()
{
uint32_t newBufferSize = GetFrameInfo().Width*GetFrameInfo().Height;
if(_bufferSize != newBufferSize) {
_frameLock.Acquire();
if(_outputBuffer) {
delete[] _outputBuffer;
}
_bufferSize = newBufferSize;
_outputBuffer = new uint32_t[newBufferSize];
_frameLock.Release();
}
}
OverscanDimensions BaseVideoFilter::GetOverscan()
{
return _overscan;
}
void BaseVideoFilter::OnBeforeApplyFilter()
{
}
bool BaseVideoFilter::IsOddFrame()
{
return _isOddFrame;
}
void BaseVideoFilter::SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber)
{
_frameLock.Acquire();
_overscan = {}; //TODO _console->GetSettings()->GetOverscanDimensions();
_isOddFrame = frameNumber % 2;
UpdateBufferSize();
OnBeforeApplyFilter();
ApplyFilter(ppuOutputBuffer);
_frameLock.Release();
}
uint32_t* BaseVideoFilter::GetOutputBuffer()
{
return _outputBuffer;
}
void BaseVideoFilter::TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream)
{
uint32_t* pngBuffer;
FrameInfo frameInfo;
uint32_t* frameBuffer = nullptr;
{
auto lock = _frameLock.AcquireSafe();
if(_bufferSize == 0 || !GetOutputBuffer()) {
return;
}
frameBuffer = new uint32_t[_bufferSize];
memcpy(frameBuffer, GetOutputBuffer(), _bufferSize * sizeof(frameBuffer[0]));
frameInfo = GetFrameInfo();
}
pngBuffer = frameBuffer;
//TODO
/*
uint32_t rotationAngle = _console->GetSettings()->GetScreenRotation();
shared_ptr<RotateFilter> rotateFilter;
if(rotationAngle > 0) {
rotateFilter.reset(new RotateFilter(rotationAngle));
pngBuffer = rotateFilter->ApplyFilter(pngBuffer, frameInfo.Width, frameInfo.Height);
frameInfo = rotateFilter->GetFrameInfo(frameInfo);
}
shared_ptr<ScaleFilter> scaleFilter = ScaleFilter::GetScaleFilter(filterType);
if(scaleFilter) {
pngBuffer = scaleFilter->ApplyFilter(pngBuffer, frameInfo.Width, frameInfo.Height, _console->GetSettings()->GetPictureSettings().ScanlineIntensity);
frameInfo = scaleFilter->GetFrameInfo(frameInfo);
}
VideoHud hud;
hud.DrawHud(_console, pngBuffer, frameInfo, _console->GetSettings()->GetOverscanDimensions());
*/
if(!filename.empty()) {
PNGHelper::WritePNG(filename, pngBuffer, frameInfo.Width, frameInfo.Height);
} else {
PNGHelper::WritePNG(*stream, pngBuffer, frameInfo.Width, frameInfo.Height);
}
delete[] frameBuffer;
}
void BaseVideoFilter::TakeScreenshot(string romName, VideoFilterType filterType)
{
string romFilename = FolderUtilities::GetFilename(romName, false);
int counter = 0;
string baseFilename = FolderUtilities::CombinePath(FolderUtilities::GetScreenshotFolder(), romFilename);
string ssFilename;
while(true) {
string counterStr = std::to_string(counter);
while(counterStr.length() < 3) {
counterStr = "0" + counterStr;
}
ssFilename = baseFilename + "_" + counterStr + ".png";
ifstream file(ssFilename, ios::in);
if(file) {
file.close();
} else {
break;
}
counter++;
}
TakeScreenshot(filterType, ssFilename);
MessageManager::DisplayMessage("ScreenshotSaved", FolderUtilities::GetFilename(ssFilename, true));
}

37
Core/BaseVideoFilter.h Normal file
View file

@ -0,0 +1,37 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/SimpleLock.h"
#include "SettingTypes.h"
class Console;
class BaseVideoFilter
{
private:
uint32_t* _outputBuffer = nullptr;
uint32_t _bufferSize = 0;
SimpleLock _frameLock;
OverscanDimensions _overscan;
bool _isOddFrame;
void UpdateBufferSize();
protected:
shared_ptr<Console> _console;
virtual void ApplyFilter(uint16_t *ppuOutputBuffer) = 0;
virtual void OnBeforeApplyFilter();
bool IsOddFrame();
public:
BaseVideoFilter(shared_ptr<Console> console);
virtual ~BaseVideoFilter();
uint32_t* GetOutputBuffer();
void SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber);
void TakeScreenshot(string romName, VideoFilterType filterType);
void TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream = nullptr);
virtual OverscanDimensions GetOverscan();
virtual FrameInfo GetFrameInfo() = 0;
};

View file

@ -3,9 +3,22 @@
#include "Cpu.h"
#include "MemoryManager.h"
#include "Debugger.h"
#include "VideoDecoder.h"
#include "VideoRenderer.h"
#include "DebugHud.h"
#include "../Utilities/Timer.h"
#include "../Utilities/VirtualFile.h"
void Console::Initialize()
{
_videoDecoder.reset(new VideoDecoder(shared_from_this()));
_videoRenderer.reset(new VideoRenderer(shared_from_this()));
_debugHud.reset(new DebugHud());
_videoDecoder->StartThread();
_videoRenderer->StartThread();
}
void Console::Run()
{
if(!_cpu) {
@ -50,6 +63,21 @@ void Console::LoadRom(VirtualFile romFile, VirtualFile patchFile)
}
}
shared_ptr<VideoRenderer> Console::GetVideoRenderer()
{
return _videoRenderer;
}
shared_ptr<VideoDecoder> Console::GetVideoDecoder()
{
return _videoDecoder;
}
shared_ptr<DebugHud> Console::GetDebugHud()
{
return _debugHud;
}
shared_ptr<Cpu> Console::GetCpu()
{
return _cpu;
@ -60,6 +88,11 @@ shared_ptr<Ppu> Console::GetPpu()
return _ppu;
}
shared_ptr<MemoryManager> Console::GetMemoryManager()
{
return _memoryManager;
}
shared_ptr<Debugger> Console::GetDebugger(bool allowStart)
{
return _debugger;

View file

@ -7,6 +7,9 @@ class Cpu;
class Ppu;
class MemoryManager;
class Debugger;
class DebugHud;
class VideoRenderer;
class VideoDecoder;
enum class MemoryOperationType;
class Console : public std::enable_shared_from_this<Console>
@ -17,17 +20,28 @@ private:
shared_ptr<MemoryManager> _memoryManager;
shared_ptr<Debugger> _debugger;
shared_ptr<VideoRenderer> _videoRenderer;
shared_ptr<VideoDecoder> _videoDecoder;
shared_ptr<DebugHud> _debugHud;
SimpleLock _runLock;
atomic<bool> _stopFlag;
public:
void Initialize();
void Run();
void Stop();
void LoadRom(VirtualFile romFile, VirtualFile patchFile);
shared_ptr<VideoRenderer> GetVideoRenderer();
shared_ptr<VideoDecoder> GetVideoDecoder();
shared_ptr<DebugHud> GetDebugHud();
shared_ptr<Cpu> GetCpu();
shared_ptr<Ppu> GetPpu();
shared_ptr<MemoryManager> GetMemoryManager();
shared_ptr<Debugger> GetDebugger(bool allowStart = true);
void ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType type);

View file

@ -45,11 +45,20 @@
<ItemGroup>
<ClInclude Include="BaseRenderer.h" />
<ClInclude Include="BaseSoundManager.h" />
<ClInclude Include="BaseVideoFilter.h" />
<ClInclude Include="Console.h" />
<ClInclude Include="Cpu.h" />
<ClInclude Include="CpuTypes.h" />
<ClInclude Include="Debugger.h" />
<ClInclude Include="DebugHud.h" />
<ClInclude Include="DefaultVideoFilter.h" />
<ClInclude Include="DisassemblyInfo.h" />
<ClInclude Include="DrawCommand.h" />
<ClInclude Include="DrawLineCommand.h" />
<ClInclude Include="DrawPixelCommand.h" />
<ClInclude Include="DrawRectangleCommand.h" />
<ClInclude Include="DrawScreenBufferCommand.h" />
<ClInclude Include="DrawStringCommand.h" />
<ClInclude Include="IAudioDevice.h" />
<ClInclude Include="IInputProvider.h" />
<ClInclude Include="IInputRecorder.h" />
@ -62,16 +71,22 @@
<ClInclude Include="MessageManager.h" />
<ClInclude Include="Ppu.h" />
<ClInclude Include="PpuTypes.h" />
<ClInclude Include="SettingTypes.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="TraceLogger.h" />
<ClInclude Include="VideoDecoder.h" />
<ClInclude Include="VideoRenderer.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="BaseRenderer.cpp" />
<ClCompile Include="BaseSoundManager.cpp" />
<ClCompile Include="BaseVideoFilter.cpp" />
<ClCompile Include="Console.cpp" />
<ClCompile Include="Cpu.cpp" />
<ClCompile Include="Cpu.Instructions.cpp" />
<ClCompile Include="Debugger.cpp" />
<ClCompile Include="DebugHud.cpp" />
<ClCompile Include="DefaultVideoFilter.cpp" />
<ClCompile Include="DisassemblyInfo.cpp" />
<ClCompile Include="KeyManager.cpp" />
<ClCompile Include="MessageManager.cpp" />
@ -89,6 +104,8 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='PGO Optimize|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="TraceLogger.cpp" />
<ClCompile Include="VideoDecoder.cpp" />
<ClCompile Include="VideoRenderer.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{78FEF1A1-6DF1-4CBB-A373-AE6FA7CE5CE0}</ProjectGuid>

View file

@ -44,9 +44,6 @@
<ClInclude Include="IRenderingDevice.h">
<Filter>Interfaces</Filter>
</ClInclude>
<ClInclude Include="BaseRenderer.h">
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="BaseSoundManager.h">
<Filter>Misc</Filter>
</ClInclude>
@ -62,6 +59,45 @@
<ClInclude Include="PpuTypes.h">
<Filter>SNES</Filter>
</ClInclude>
<ClInclude Include="VideoRenderer.h">
<Filter>Video</Filter>
</ClInclude>
<ClInclude Include="VideoDecoder.h">
<Filter>Video</Filter>
</ClInclude>
<ClInclude Include="BaseRenderer.h">
<Filter>Video</Filter>
</ClInclude>
<ClInclude Include="DefaultVideoFilter.h">
<Filter>Video</Filter>
</ClInclude>
<ClInclude Include="BaseVideoFilter.h">
<Filter>Video</Filter>
</ClInclude>
<ClInclude Include="SettingTypes.h">
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="DebugHud.h">
<Filter>Video\DebugHud</Filter>
</ClInclude>
<ClInclude Include="DrawCommand.h">
<Filter>Video\DebugHud</Filter>
</ClInclude>
<ClInclude Include="DrawLineCommand.h">
<Filter>Video\DebugHud</Filter>
</ClInclude>
<ClInclude Include="DrawPixelCommand.h">
<Filter>Video\DebugHud</Filter>
</ClInclude>
<ClInclude Include="DrawRectangleCommand.h">
<Filter>Video\DebugHud</Filter>
</ClInclude>
<ClInclude Include="DrawScreenBufferCommand.h">
<Filter>Video\DebugHud</Filter>
</ClInclude>
<ClInclude Include="DrawStringCommand.h">
<Filter>Video\DebugHud</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -80,9 +116,6 @@
<ClCompile Include="Console.cpp">
<Filter>SNES</Filter>
</ClCompile>
<ClCompile Include="BaseRenderer.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="BaseSoundManager.cpp">
<Filter>Misc</Filter>
</ClCompile>
@ -98,6 +131,24 @@
<ClCompile Include="Ppu.cpp">
<Filter>SNES</Filter>
</ClCompile>
<ClCompile Include="VideoDecoder.cpp">
<Filter>Video</Filter>
</ClCompile>
<ClCompile Include="VideoRenderer.cpp">
<Filter>Video</Filter>
</ClCompile>
<ClCompile Include="BaseRenderer.cpp">
<Filter>Video</Filter>
</ClCompile>
<ClCompile Include="DefaultVideoFilter.cpp">
<Filter>Video</Filter>
</ClCompile>
<ClCompile Include="BaseVideoFilter.cpp">
<Filter>Video</Filter>
</ClCompile>
<ClCompile Include="DebugHud.cpp">
<Filter>Video\DebugHud</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">
@ -112,5 +163,11 @@
<Filter Include="Misc">
<UniqueIdentifier>{0da6a615-3092-40f7-936c-253eb03a8784}</UniqueIdentifier>
</Filter>
<Filter Include="Video">
<UniqueIdentifier>{7aa5124a-c2c1-418e-b7bb-5ba7846aa9b0}</UniqueIdentifier>
</Filter>
<Filter Include="Video\DebugHud">
<UniqueIdentifier>{0d0cf24e-4126-4bfb-b391-27a015e7722a}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

74
Core/DebugHud.cpp Normal file
View file

@ -0,0 +1,74 @@
#include "stdafx.h"
#include <algorithm>
#include "DebugHud.h"
#include "DrawCommand.h"
#include "DrawLineCommand.h"
#include "DrawPixelCommand.h"
#include "DrawRectangleCommand.h"
#include "DrawStringCommand.h"
#include "DrawScreenBufferCommand.h"
DebugHud::DebugHud()
{
}
DebugHud::~DebugHud()
{
_commandLock.Acquire();
_commandLock.Release();
}
void DebugHud::ClearScreen()
{
auto lock = _commandLock.AcquireSafe();
_commands.clear();
}
void DebugHud::Draw(uint32_t* argbBuffer, OverscanDimensions overscan, uint32_t lineWidth, uint32_t frameNumber)
{
auto lock = _commandLock.AcquireSafe();
for(unique_ptr<DrawCommand> &command : _commands) {
command->Draw(argbBuffer, overscan, lineWidth, frameNumber);
}
_commands.erase(std::remove_if(_commands.begin(), _commands.end(), [](const unique_ptr<DrawCommand>& c) { return c->Expired(); }), _commands.end());
}
void DebugHud::DrawPixel(int x, int y, int color, int frameCount, int startFrame)
{
auto lock = _commandLock.AcquireSafe();
if(_commands.size() < DebugHud::MaxCommandCount) {
_commands.push_back(unique_ptr<DrawPixelCommand>(new DrawPixelCommand(x, y, color, frameCount, startFrame)));
}
}
void DebugHud::DrawLine(int x, int y, int x2, int y2, int color, int frameCount, int startFrame)
{
auto lock = _commandLock.AcquireSafe();
if(_commands.size() < DebugHud::MaxCommandCount) {
_commands.push_back(unique_ptr<DrawLineCommand>(new DrawLineCommand(x, y, x2, y2, color, frameCount, startFrame)));
}
}
void DebugHud::DrawRectangle(int x, int y, int width, int height, int color, bool fill, int frameCount, int startFrame)
{
auto lock = _commandLock.AcquireSafe();
if(_commands.size() < DebugHud::MaxCommandCount) {
_commands.push_back(unique_ptr<DrawRectangleCommand>(new DrawRectangleCommand(x, y, width, height, color, fill, frameCount, startFrame)));
}
}
void DebugHud::DrawScreenBuffer(uint32_t* screenBuffer, int startFrame)
{
auto lock = _commandLock.AcquireSafe();
if(_commands.size() < DebugHud::MaxCommandCount) {
_commands.push_back(unique_ptr<DrawScreenBufferCommand>(new DrawScreenBufferCommand(screenBuffer, startFrame)));
}
}
void DebugHud::DrawString(int x, int y, string text, int color, int backColor, int frameCount, int startFrame)
{
auto lock = _commandLock.AcquireSafe();
if(_commands.size() < DebugHud::MaxCommandCount) {
_commands.push_back(unique_ptr<DrawStringCommand>(new DrawStringCommand(x, y, text, color, backColor, frameCount, startFrame)));
}
}

27
Core/DebugHud.h Normal file
View file

@ -0,0 +1,27 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/SimpleLock.h"
#include "SettingTypes.h"
class DrawCommand;
class DebugHud
{
private:
static constexpr size_t MaxCommandCount = 500000;
vector<unique_ptr<DrawCommand>> _commands;
SimpleLock _commandLock;
public:
DebugHud();
~DebugHud();
void Draw(uint32_t* argbBuffer, OverscanDimensions overscan, uint32_t width, uint32_t frameNumber);
void ClearScreen();
void DrawPixel(int x, int y, int color, int frameCount, int startFrame);
void DrawLine(int x, int y, int x2, int y2, int color, int frameCount, int startFrame);
void DrawRectangle(int x, int y, int width, int height, int color, bool fill, int frameCount, int startFrame);
void DrawScreenBuffer(uint32_t* screenBuffer, int startFrame);
void DrawString(int x, int y, string text, int color, int backColor, int frameCount, int startFrame);
};

View file

@ -14,7 +14,7 @@ Debugger::Debugger(shared_ptr<Cpu> cpu, shared_ptr<Ppu> ppu, shared_ptr<MemoryMa
_memoryManager = memoryManager;
_traceLogger.reset(new TraceLogger(this, memoryManager));
_cpuStepCount = 1;
_cpuStepCount = 0;
}
Debugger::~Debugger()

136
Core/DefaultVideoFilter.cpp Normal file
View file

@ -0,0 +1,136 @@
#include "stdafx.h"
#include "DefaultVideoFilter.h"
#define _USE_MATH_DEFINES
#include <math.h>
#include <algorithm>
#include "DebugHud.h"
#include "Console.h"
DefaultVideoFilter::DefaultVideoFilter(shared_ptr<Console> console) : BaseVideoFilter(console)
{
InitConversionMatrix(_pictureSettings.Hue, _pictureSettings.Saturation);
}
void DefaultVideoFilter::InitConversionMatrix(double hueShift, double saturationShift)
{
_pictureSettings.Hue = hueShift;
_pictureSettings.Saturation = saturationShift;
double hue = hueShift * M_PI;
double sat = saturationShift + 1;
double baseValues[6] = { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f };
double s = sin(hue) * sat;
double c = cos(hue) * sat;
double *output = _yiqToRgbMatrix;
double *input = baseValues;
for(int n = 0; n < 3; n++) {
double i = *input++;
double q = *input++;
*output++ = i * c - q * s;
*output++ = i * s + q * c;
}
}
FrameInfo DefaultVideoFilter::GetFrameInfo()
{
OverscanDimensions overscan = GetOverscan();
return { overscan.GetScreenWidth(), overscan.GetScreenHeight(), OverscanDimensions::ScreenWidth, OverscanDimensions::ScreenHeight, 4 };
}
void DefaultVideoFilter::OnBeforeApplyFilter()
{
/*PictureSettings currentSettings = _console->GetSettings()->GetPictureSettings();
if(_pictureSettings.Hue != currentSettings.Hue || _pictureSettings.Saturation != currentSettings.Saturation) {
InitConversionMatrix(currentSettings.Hue, currentSettings.Saturation);
}
_pictureSettings = currentSettings;
_needToProcess = _pictureSettings.Hue != 0 || _pictureSettings.Saturation != 0 || _pictureSettings.Brightness || _pictureSettings.Contrast;
if(_needToProcess) {
double y, i, q;
uint32_t* originalPalette = _console->GetSettings()->GetRgbPalette();
for(int pal = 0; pal < 512; pal++) {
uint32_t pixelOutput = originalPalette[pal];
double redChannel = ((pixelOutput & 0xFF0000) >> 16) / 255.0;
double greenChannel = ((pixelOutput & 0xFF00) >> 8) / 255.0;
double blueChannel = (pixelOutput & 0xFF) / 255.0;
//Apply brightness, contrast, hue & saturation
RgbToYiq(redChannel, greenChannel, blueChannel, y, i, q);
y *= _pictureSettings.Contrast * 0.5f + 1;
y += _pictureSettings.Brightness * 0.5f;
YiqToRgb(y, i, q, redChannel, greenChannel, blueChannel);
int r = std::min(255, (int)(redChannel * 255));
int g = std::min(255, (int)(greenChannel * 255));
int b = std::min(255, (int)(blueChannel * 255));
_calculatedPalette[pal] = 0xFF000000 | (r << 16) | (g << 8) | b;
}
} else {
memcpy(_calculatedPalette, _console->GetSettings()->GetRgbPalette(), sizeof(_calculatedPalette));
}*/
}
void DefaultVideoFilter::DecodePpuBuffer(uint16_t *ppuOutputBuffer, uint32_t* outputBuffer, bool displayScanlines)
{
/*uint32_t* out = outputBuffer;
OverscanDimensions overscan = GetOverscan();
uint8_t scanlineIntensity = (uint8_t)((1.0 - _console->GetSettings()->GetPictureSettings().ScanlineIntensity) * 255);
for(uint32_t i = overscan.Top, iMax = 240 - overscan.Bottom; i < iMax; i++) {
if(displayScanlines && (i + overscan.Top) % 2 == 0) {
for(uint32_t j = overscan.Left, jMax = 256 - overscan.Right; j < jMax; j++) {
*out = ApplyScanlineEffect(ppuOutputBuffer[i * 256 + j], scanlineIntensity);
out++;
}
} else {
for(uint32_t j = overscan.Left, jMax = 256 - overscan.Right; j < jMax; j++) {
*out = _calculatedPalette[ppuOutputBuffer[i * 256 + j]];
out++;
}
}
}*/
}
void DefaultVideoFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
{
uint32_t *out = GetOutputBuffer();
for(int i = 0; i < 256 * 224; i++) {
uint16_t rgb555 = ppuOutputBuffer[i];
uint8_t b = (rgb555 >> 10) * 256 / 32;
uint8_t g = ((rgb555 >> 5) & 0x1F) * 256 / 32;
uint8_t r = (rgb555 & 0x1F) * 256 / 32;
out[i] = 0xFF000000 | (r << 16) | (g << 8) | b;
}
//DecodePpuBuffer(ppuOutputBuffer, GetOutputBuffer(), _console->GetSettings()->GetVideoFilterType() <= VideoFilterType::BisqwitNtsc);
}
void DefaultVideoFilter::RgbToYiq(double r, double g, double b, double &y, double &i, double &q)
{
y = r * 0.299f + g * 0.587f + b * 0.114f;
i = r * 0.596f - g * 0.275f - b * 0.321f;
q = r * 0.212f - g * 0.523f + b * 0.311f;
}
void DefaultVideoFilter::YiqToRgb(double y, double i, double q, double &r, double &g, double &b)
{
r = std::max(0.0, std::min(1.0, (y + _yiqToRgbMatrix[0] * i + _yiqToRgbMatrix[1] * q)));
g = std::max(0.0, std::min(1.0, (y + _yiqToRgbMatrix[2] * i + _yiqToRgbMatrix[3] * q)));
b = std::max(0.0, std::min(1.0, (y + _yiqToRgbMatrix[4] * i + _yiqToRgbMatrix[5] * q)));
}
uint32_t DefaultVideoFilter::ApplyScanlineEffect(uint16_t ppuPixel, uint8_t scanlineIntensity)
{
uint32_t pixelOutput = _calculatedPalette[ppuPixel];
uint8_t r = ((pixelOutput & 0xFF0000) >> 16) * scanlineIntensity / 255;
uint8_t g = ((pixelOutput & 0xFF00) >> 8) * scanlineIntensity / 255;
uint8_t b = (pixelOutput & 0xFF) * scanlineIntensity / 255;
return 0xFF000000 | (r << 16) | (g << 8) | b;
}

28
Core/DefaultVideoFilter.h Normal file
View file

@ -0,0 +1,28 @@
#pragma once
#include "stdafx.h"
#include "BaseVideoFilter.h"
class DefaultVideoFilter : public BaseVideoFilter
{
private:
double _yiqToRgbMatrix[6];
uint32_t _calculatedPalette[512];
PictureSettings _pictureSettings;
bool _needToProcess = false;
void InitConversionMatrix(double hueShift, double saturationShift);
void RgbToYiq(double r, double g, double b, double &y, double &i, double &q);
void YiqToRgb(double y, double i, double q, double &r, double &g, double &b);
protected:
void DecodePpuBuffer(uint16_t *ppuOutputBuffer, uint32_t* outputBuffer, bool displayScanlines);
uint32_t ApplyScanlineEffect(uint16_t ppuPixel, uint8_t scanlineIntensity);
void OnBeforeApplyFilter();
public:
DefaultVideoFilter(shared_ptr<Console> console);
void ApplyFilter(uint16_t *ppuOutputBuffer);
FrameInfo GetFrameInfo();
};

98
Core/DrawCommand.h Normal file
View file

@ -0,0 +1,98 @@
#pragma once
#include "stdafx.h"
#include "SettingTypes.h"
#include "Console.h"
class DrawCommand
{
private:
int _frameCount;
uint32_t* _argbBuffer;
OverscanDimensions _overscan;
uint32_t _lineWidth;
uint32_t _startFrame;
protected:
bool _useIntegerScaling;
float _xScale;
int _yScale;
virtual void InternalDraw() = 0;
__forceinline void DrawPixel(uint32_t x, uint32_t y, int color)
{
if(x < _overscan.Left || x >= _overscan.Left + _overscan.GetScreenWidth() ||
y < _overscan.Top || y >= _overscan.Top + _overscan.GetScreenHeight()) {
//In overscan (out of bounds), skip drawing
return;
}
uint32_t alpha = (color & 0xFF000000);
if(alpha > 0) {
if(_yScale == 1) {
if(alpha != 0xFF000000) {
BlendColors((uint8_t*)&_argbBuffer[(y - _overscan.Top)*_lineWidth + (x - _overscan.Left)], (uint8_t*)&color);
} else {
_argbBuffer[(y - _overscan.Top)*_lineWidth + (x - _overscan.Left)] = color;
}
} else {
int xPixelCount = _useIntegerScaling ? _yScale : (int)((x + 1)*_xScale) - (int)(x*_xScale);
x = (int)(x * (_useIntegerScaling ? _yScale : _xScale));
y = (int)(y * _yScale);
int top = (int)(_overscan.Top * _yScale);
int left = (int)(_overscan.Left * _xScale);
for(int i = 0; i < _yScale; i++) {
for(int j = 0; j < xPixelCount; j++) {
if(alpha != 0xFF000000) {
BlendColors((uint8_t*)&_argbBuffer[(y - top)*_lineWidth + i*_lineWidth + (x - left)+j], (uint8_t*)&color);
} else {
_argbBuffer[(y - top)*_lineWidth + i*_lineWidth + (x - left) +j] = color;
}
}
}
}
}
}
__forceinline void BlendColors(uint8_t output[4], uint8_t input[4])
{
uint8_t alpha = input[3] + 1;
uint8_t invertedAlpha = 256 - input[3];
output[0] = (uint8_t)((alpha * input[0] + invertedAlpha * output[0]) >> 8);
output[1] = (uint8_t)((alpha * input[1] + invertedAlpha * output[1]) >> 8);
output[2] = (uint8_t)((alpha * input[2] + invertedAlpha * output[2]) >> 8);
output[3] = 0xFF;
}
public:
DrawCommand(int startFrame, int frameCount, bool useIntegerScaling = false)
{
_frameCount = frameCount > 0 ? frameCount : 1;
_startFrame = startFrame;
_useIntegerScaling = useIntegerScaling;
}
virtual ~DrawCommand()
{
}
void Draw(uint32_t* argbBuffer, OverscanDimensions &overscan, uint32_t lineWidth, uint32_t frameNumber)
{
if(_startFrame <= frameNumber) {
_argbBuffer = argbBuffer;
_overscan = overscan;
_lineWidth = lineWidth;
_yScale = lineWidth / overscan.GetScreenWidth();
_xScale = (float)lineWidth / overscan.GetScreenWidth();
InternalDraw();
_frameCount--;
}
}
bool Expired()
{
return _frameCount <= 0;
}
};

42
Core/DrawLineCommand.h Normal file
View file

@ -0,0 +1,42 @@
#pragma once
#include "stdafx.h"
#include "DrawCommand.h"
class DrawLineCommand : public DrawCommand
{
private:
int _x, _y, _x2, _y2, _color;
protected:
void InternalDraw()
{
int x = _x;
int y = _y;
int dx = abs(_x2 - x), sx = x < _x2 ? 1 : -1;
int dy = abs(_y2 - y), sy = y < _y2 ? 1 : -1;
int err = (dx > dy ? dx : -dy) / 2, e2;
while(true) {
DrawPixel(x, y, _color);
if(x == _x2 && y == _y2) {
break;
}
e2 = err;
if(e2 > -dx) {
err -= dy; x += sx;
}
if(e2 < dy) {
err += dx; y += sy;
}
}
}
public:
DrawLineCommand(int x, int y, int x2, int y2, int color, int frameCount, int startFrame) :
DrawCommand(startFrame, frameCount), _x(x), _y(y), _x2(x2), _y2(y2), _color(color)
{
//Invert alpha byte - 0 = opaque, 255 = transparent (this way, no need to specifiy alpha channel all the time)
_color = (~color & 0xFF000000) | (color & 0xFFFFFF);
}
};

23
Core/DrawPixelCommand.h Normal file
View file

@ -0,0 +1,23 @@
#pragma once
#include "stdafx.h"
#include "DrawCommand.h"
class DrawPixelCommand : public DrawCommand
{
private:
int _x, _y, _color;
protected:
void InternalDraw()
{
DrawPixel(_x, _y, _color);
}
public:
DrawPixelCommand(int x, int y, int color, int frameCount, int startFrame) :
DrawCommand(startFrame, frameCount), _x(x), _y(y), _color(color)
{
//Invert alpha byte - 0 = opaque, 255 = transparent (this way, no need to specifiy alpha channel all the time)
_color = (~color & 0xFF000000) | (color & 0xFFFFFF);
}
};

View file

@ -0,0 +1,48 @@
#pragma once
#include "stdafx.h"
#include "DrawCommand.h"
class DrawRectangleCommand : public DrawCommand
{
private:
int _x, _y, _width, _height, _color;
bool _fill;
protected:
void InternalDraw()
{
if(_fill) {
for(int j = 0; j < _height; j++) {
for(int i = 0; i < _width; i++) {
DrawPixel(_x + i, _y + j, _color);
}
}
} else {
for(int i = 0; i < _width; i++) {
DrawPixel(_x + i, _y, _color);
DrawPixel(_x + i, _y + _height - 1, _color);
}
for(int i = 1; i < _height - 1; i++) {
DrawPixel(_x, _y + i, _color);
DrawPixel(_x + _width - 1, _y + i, _color);
}
}
}
public:
DrawRectangleCommand(int x, int y, int width, int height, int color, bool fill, int frameCount, int startFrame) :
DrawCommand(startFrame, frameCount), _x(x), _y(y), _width(width), _height(height), _color(color), _fill(fill)
{
if(width < 0) {
_x += width + 1;
_width = -width;
}
if(height < 0) {
_y += height + 1;
_height = -height;
}
//Invert alpha byte - 0 = opaque, 255 = transparent (this way, no need to specifiy alpha channel all the time)
_color = (~color & 0xFF000000) | (color & 0xFFFFFF);
}
};

View file

@ -0,0 +1,25 @@
#pragma once
#include "stdafx.h"
#include "DrawCommand.h"
class DrawScreenBufferCommand : public DrawCommand
{
private:
uint32_t _screenBuffer[256*240];
protected:
void InternalDraw()
{
for(int y = 0; y < 240; y++) {
for(int x = 0; x < 256; x++) {
DrawPixel(x, y, _screenBuffer[(y << 8) + x]);
}
}
}
public:
DrawScreenBufferCommand(uint32_t* screenBuffer, int startFrame) : DrawCommand(startFrame, 1)
{
memcpy(_screenBuffer, screenBuffer, 256 * 240 * sizeof(uint32_t));
}
};

161
Core/DrawStringCommand.h Normal file
View file

@ -0,0 +1,161 @@
#pragma once
#include "stdafx.h"
#include "DrawCommand.h"
class DrawStringCommand : public DrawCommand
{
private:
int _x, _y, _color, _backColor;
string _text;
//Taken from FCEUX's LUA code
const int _tabSpace = 4;
const uint8_t _font[792] = {
6, 0, 0, 0, 0, 0, 0, 0, // 0x20 - Spacebar
3, 64, 64, 64, 64, 64, 0, 64,
5, 80, 80, 80, 0, 0, 0, 0,
6, 80, 80,248, 80,248, 80, 80,
6, 32,120,160,112, 40,240, 32,
6, 64,168, 80, 32, 80,168, 16,
6, 96,144,160, 64,168,144,104,
3, 64, 64, 0, 0, 0, 0, 0,
4, 32, 64, 64, 64, 64, 64, 32,
4, 64, 32, 32, 32, 32, 32, 64,
6, 0, 80, 32,248, 32, 80, 0,
6, 0, 32, 32,248, 32, 32, 0,
3, 0, 0, 0, 0, 0, 64,128,
5, 0, 0, 0,240, 0, 0, 0,
3, 0, 0, 0, 0, 0, 0, 64,
5, 16, 16, 32, 32, 32, 64, 64,
6,112,136,136,136,136,136,112, // 0x30 - 0
6, 32, 96, 32, 32, 32, 32, 32,
6,112,136, 8, 48, 64,128,248,
6,112,136, 8, 48, 8,136,112,
6, 16, 48, 80,144,248, 16, 16,
6,248,128,128,240, 8, 8,240,
6, 48, 64,128,240,136,136,112,
6,248, 8, 16, 16, 32, 32, 32,
6,112,136,136,112,136,136,112,
6,112,136,136,120, 8, 16, 96,
3, 0, 0, 64, 0, 0, 64, 0,
3, 0, 0, 64, 0, 0, 64,128,
4, 0, 32, 64,128, 64, 32, 0,
5, 0, 0,240, 0,240, 0, 0,
4, 0,128, 64, 32, 64,128, 0,
6,112,136, 8, 16, 32, 0, 32, // 0x3F - ?
6,112,136,136,184,176,128,112, // 0x40 - @
6,112,136,136,248,136,136,136, // 0x41 - A
6,240,136,136,240,136,136,240,
6,112,136,128,128,128,136,112,
6,224,144,136,136,136,144,224,
6,248,128,128,240,128,128,248,
6,248,128,128,240,128,128,128,
6,112,136,128,184,136,136,120,
6,136,136,136,248,136,136,136,
4,224, 64, 64, 64, 64, 64,224,
6, 8, 8, 8, 8, 8,136,112,
6,136,144,160,192,160,144,136,
6,128,128,128,128,128,128,248,
6,136,216,168,168,136,136,136,
6,136,136,200,168,152,136,136,
7, 48, 72,132,132,132, 72, 48,
6,240,136,136,240,128,128,128,
6,112,136,136,136,168,144,104,
6,240,136,136,240,144,136,136,
6,112,136,128,112, 8,136,112,
6,248, 32, 32, 32, 32, 32, 32,
6,136,136,136,136,136,136,112,
6,136,136,136, 80, 80, 32, 32,
6,136,136,136,136,168,168, 80,
6,136,136, 80, 32, 80,136,136,
6,136,136, 80, 32, 32, 32, 32,
6,248, 8, 16, 32, 64,128,248,
3,192,128,128,128,128,128,192,
5, 64, 64, 32, 32, 32, 16, 16,
3,192, 64, 64, 64, 64, 64,192,
4, 64,160, 0, 0, 0, 0, 0,
6, 0, 0, 0, 0, 0, 0,248,
3,128, 64, 0, 0, 0, 0, 0,
5, 0, 0, 96, 16,112,144,112, // 0x61 - a
5,128,128,224,144,144,144,224,
5, 0, 0,112,128,128,128,112,
5, 16, 16,112,144,144,144,112,
5, 0, 0, 96,144,240,128,112,
5, 48, 64,224, 64, 64, 64, 64,
5, 0,112,144,144,112, 16,224,
5,128,128,224,144,144,144,144,
2,128, 0,128,128,128,128,128,
4, 32, 0, 32, 32, 32, 32,192,
5,128,128,144,160,192,160,144,
2,128,128,128,128,128,128,128,
6, 0, 0,208,168,168,168,168,
5, 0, 0,224,144,144,144,144,
5, 0, 0, 96,144,144,144, 96,
5, 0,224,144,144,224,128,128,
5, 0,112,144,144,112, 16, 16,
5, 0, 0,176,192,128,128,128,
5, 0, 0,112,128, 96, 16,224,
4, 64, 64,224, 64, 64, 64, 32,
5, 0, 0,144,144,144,144,112,
5, 0, 0,144,144,144,160,192,
6, 0, 0,136,136,168,168, 80,
5, 0, 0,144,144, 96,144,144,
5, 0,144,144,144,112, 16, 96,
5, 0, 0,240, 32, 64,128,240,
4, 32, 64, 64,128, 64, 64, 32,
3, 64, 64, 64, 64, 64, 64, 64,
4,128, 64, 64, 32, 64, 64,128,
6, 0,104,176, 0, 0, 0, 0
};
int GetCharNumber(char ch)
{
ch -= 32;
return (ch < 0 || ch > 98) ? 0 : ch;
}
int GetCharWidth(char ch)
{
return _font[GetCharNumber(ch) * 8];
}
protected:
void InternalDraw()
{
int startX = (int)(_x * _xScale / _yScale);
int x = startX;
int y = _y;
for(char c : _text) {
if(c == '\n') {
x = startX;
y += 9;
} else if(c == '\t') {
x += (_tabSpace - (((x - startX) / 8) % _tabSpace)) * 8;
} else {
int ch = GetCharNumber(c);
int width = GetCharWidth(c);
int rowOffset = (c == 'y' || c == 'g' || c == 'p' || c == 'q') ? 1 : 0;
for(int j = 0; j < 8; j++) {
uint8_t rowData = ((j == 7 && rowOffset == 0) || (j == 0 && rowOffset == 1)) ? 0 : _font[ch * 8 + 1 + j - rowOffset];
for(int i = 0; i < width; i++) {
int drawFg = (rowData >> (7 - i)) & 0x01;
DrawPixel(x + i, y + j, drawFg ? _color : _backColor);
}
}
for(int i = 0; i < width; i++) {
DrawPixel(x + i, y - 1, _backColor);
}
x += width;
}
}
}
public:
DrawStringCommand(int x, int y, string text, int color, int backColor, int frameCount, int startFrame) :
DrawCommand(startFrame, frameCount, true), _x(x), _y(y), _color(color), _backColor(backColor), _text(text)
{
//Invert alpha byte - 0 = opaque, 255 = transparent (this way, no need to specifiy alpha channel all the time)
_color = (~color & 0xFF000000) | (color & 0xFFFFFF);
_backColor = (~backColor & 0xFF000000) | (backColor & 0xFFFFFF);
}
};

129
Core/SettingTypes.h Normal file
View file

@ -0,0 +1,129 @@
#pragma once
#include "stdafx.h"
enum class ScaleFilterType
{
xBRZ = 0,
HQX = 1,
Scale2x = 2,
_2xSai = 3,
Super2xSai = 4,
SuperEagle = 5,
Prescale = 6,
};
enum class VideoFilterType
{
None = 0,
NTSC = 1,
BisqwitNtscQuarterRes = 2,
BisqwitNtscHalfRes = 3,
BisqwitNtsc = 4,
xBRZ2x = 5,
xBRZ3x = 6,
xBRZ4x = 7,
xBRZ5x = 8,
xBRZ6x = 9,
HQ2x = 10,
HQ3x = 11,
HQ4x = 12,
Scale2x = 13,
Scale3x = 14,
Scale4x = 15,
_2xSai = 16,
Super2xSai = 17,
SuperEagle = 18,
Prescale2x = 19,
Prescale3x = 20,
Prescale4x = 21,
Prescale6x = 22,
Prescale8x = 23,
Prescale10x = 24,
Raw = 25,
HdPack = 999
};
enum class VideoResizeFilter
{
NearestNeighbor = 0,
Bilinear = 1
};
enum class VideoAspectRatio
{
NoStretching = 0,
Auto = 1,
NTSC = 2,
PAL = 3,
Standard = 4,
Widescreen = 5,
Custom = 6
};
struct OverscanDimensions
{
static const int ScreenWidth = 256;
static const int ScreenHeight = 224;
uint32_t Left = 0;
uint32_t Right = 0;
uint32_t Top = 0;
uint32_t Bottom = 0;
uint32_t GetPixelCount()
{
return GetScreenWidth() * GetScreenHeight();
}
uint32_t GetScreenWidth()
{
return 256 - Left - Right;
}
uint32_t GetScreenHeight()
{
return 224 - Top - Bottom;
}
};
struct PictureSettings
{
double Brightness = 0;
double Contrast = 0;
double Saturation = 0;
double Hue = 0;
double ScanlineIntensity = 0;
};
struct NtscFilterSettings
{
double Sharpness = 0;
double Gamma = 0;
double Resolution = 0;
double Artifacts = 0;
double Fringing = 0;
double Bleed = 0;
bool MergeFields = false;
bool VerticalBlend = false;
bool KeepVerticalResolution = false;
double YFilterLength = 0;
double IFilterLength = 0;
double QFilterLength = 0;
};
struct FrameInfo
{
uint32_t Width;
uint32_t Height;
uint32_t OriginalWidth;
uint32_t OriginalHeight;
uint32_t BitsPerPixel;
};
struct ScreenSize
{
int32_t Width;
int32_t Height;
double Scale;
};

240
Core/VideoDecoder.cpp Normal file
View file

@ -0,0 +1,240 @@
#include "stdafx.h"
#include "IRenderingDevice.h"
#include "VideoDecoder.h"
#include "VideoRenderer.h"
#include "DefaultVideoFilter.h"
#include "Console.h"
#include "Ppu.h"
#include "DebugHud.h"
VideoDecoder::VideoDecoder(shared_ptr<Console> console)
{
_console = console;
_frameChanged = false;
_stopFlag = false;
UpdateVideoFilter();
}
VideoDecoder::~VideoDecoder()
{
StopThread();
}
FrameInfo VideoDecoder::GetFrameInfo()
{
return _lastFrameInfo;
}
void VideoDecoder::GetScreenSize(ScreenSize &size, bool ignoreScale)
{
if(_videoFilter) {
/*OverscanDimensions overscan = ignoreScale ? _videoFilter->GetOverscan() : _console->GetSettings()->GetOverscanDimensions();
FrameInfo frameInfo{ overscan.GetScreenWidth(), overscan.GetScreenHeight(), PPU::ScreenWidth, PPU::ScreenHeight, 4 };
double aspectRatio = _console->GetSettings()->GetAspectRatio(_console);
double scale = (ignoreScale ? 1 : _console->GetSettings()->GetVideoScale());
size.Width = (int32_t)(frameInfo.Width * scale);
size.Height = (int32_t)(frameInfo.Height * scale);
if(aspectRatio != 0.0) {
size.Width = (uint32_t)(frameInfo.OriginalHeight * scale * aspectRatio * ((double)frameInfo.Width / frameInfo.OriginalWidth));
}
if(_console->GetSettings()->GetScreenRotation() % 180) {
std::swap(size.Width, size.Height);
}
size.Scale = scale;*/
if(true || ignoreScale) {
size.Width = 256;
size.Height = 224;
} else {
size.Width = 512;
size.Height = 448;
}
size.Scale = 2;
}
}
void VideoDecoder::UpdateVideoFilter()
{
VideoFilterType newFilter = VideoFilterType::None;
if(_videoFilterType != newFilter || _videoFilter == nullptr) {
_videoFilterType = newFilter;
_videoFilter.reset(new DefaultVideoFilter(_console));
//_scaleFilter.reset();
switch(_videoFilterType) {
case VideoFilterType::None: break;
//TODO
/*
case VideoFilterType::NTSC: _videoFilter.reset(new NtscFilter(_console)); break;
case VideoFilterType::BisqwitNtsc: _videoFilter.reset(new BisqwitNtscFilter(_console, 1)); break;
case VideoFilterType::BisqwitNtscHalfRes: _videoFilter.reset(new BisqwitNtscFilter(_console, 2)); break;
case VideoFilterType::BisqwitNtscQuarterRes: _videoFilter.reset(new BisqwitNtscFilter(_console, 4)); break;
case VideoFilterType::Raw: _videoFilter.reset(new RawVideoFilter(_console)); break;
default: _scaleFilter = ScaleFilter::GetScaleFilter(_videoFilterType); break;*/
}
}
//TODO
/*
if(_console->GetSettings()->GetScreenRotation() == 0 && _rotateFilter) {
_rotateFilter.reset();
} else if(_console->GetSettings()->GetScreenRotation() > 0) {
if(!_rotateFilter || _rotateFilter->GetAngle() != _console->GetSettings()->GetScreenRotation()) {
_rotateFilter.reset(new RotateFilter(_console->GetSettings()->GetScreenRotation()));
}
}*/
}
void VideoDecoder::DecodeFrame(bool synchronous)
{
UpdateVideoFilter();
_videoFilter->SendFrame(_ppuOutputBuffer, _frameNumber);
uint32_t* outputBuffer = _videoFilter->GetOutputBuffer();
FrameInfo frameInfo = _videoFilter->GetFrameInfo();
_console->GetDebugHud()->Draw(outputBuffer, _videoFilter->GetOverscan(), frameInfo.Width, _frameNumber);
/*
if(_rotateFilter) {
outputBuffer = _rotateFilter->ApplyFilter(outputBuffer, frameInfo.Width, frameInfo.Height);
frameInfo = _rotateFilter->GetFrameInfo(frameInfo);
}
if(_scaleFilter) {
outputBuffer = _scaleFilter->ApplyFilter(outputBuffer, frameInfo.Width, frameInfo.Height, _console->GetSettings()->GetPictureSettings().ScanlineIntensity);
frameInfo = _scaleFilter->GetFrameInfo(frameInfo);
}
if(_hud) {
_hud->DrawHud(_console, outputBuffer, frameInfo, _videoFilter->GetOverscan());
}*/
ScreenSize screenSize;
GetScreenSize(screenSize, true);
/*if(_previousScale != _console->GetSettings()->GetVideoScale() || screenSize.Height != _previousScreenSize.Height || screenSize.Width != _previousScreenSize.Width) {
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::ResolutionChanged);
}*/
_previousScale = 1; // _console->GetSettings()->GetVideoScale();
_previousScreenSize = screenSize;
_lastFrameInfo = frameInfo;
_frameChanged = false;
//Rewind manager will take care of sending the correct frame to the video renderer
_console->GetVideoRenderer()->UpdateFrame(outputBuffer, frameInfo.Width, frameInfo.Height);
}
void VideoDecoder::DecodeThread()
{
//This thread will decode the PPU's output (color ID to RGB, intensify r/g/b and produce a HD version of the frame if needed)
while(!_stopFlag.load()) {
//DecodeFrame returns the final ARGB frame we want to display in the emulator window
while(!_frameChanged) {
_waitForFrame.Wait();
if(_stopFlag.load()) {
return;
}
}
DecodeFrame();
}
}
uint32_t VideoDecoder::GetFrameCount()
{
return _frameCount;
}
void VideoDecoder::UpdateFrameSync(uint16_t *ppuOutputBuffer, uint32_t frameNumber)
{
_frameNumber = frameNumber;
_ppuOutputBuffer = ppuOutputBuffer;
DecodeFrame(true);
_frameCount++;
}
void VideoDecoder::UpdateFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber)
{
if(_frameChanged) {
//Last frame isn't done decoding yet - sometimes Signal() introduces a 25-30ms delay
while(_frameChanged) {
//Spin until decode is done
}
//At this point, we are sure that the decode thread is no longer busy
}
_frameNumber = frameNumber;
_ppuOutputBuffer = ppuOutputBuffer;
_frameChanged = true;
_waitForFrame.Signal();
_frameCount++;
}
void VideoDecoder::StartThread()
{
#ifndef LIBRETRO
if(!_decodeThread) {
_stopFlag = false;
_frameChanged = false;
_frameCount = 0;
_waitForFrame.Reset();
//TODO
//_hud.reset(new VideoHud());
_decodeThread.reset(new thread(&VideoDecoder::DecodeThread, this));
}
#endif
}
void VideoDecoder::StopThread()
{
#ifndef LIBRETRO
_stopFlag = true;
if(_decodeThread) {
_waitForFrame.Signal();
_decodeThread->join();
_decodeThread.reset();
//TODO
//_hud.reset();
/*UpdateVideoFilter();
if(_ppuOutputBuffer != nullptr) {
//Clear whole screen
for(uint32_t i = 0; i < PPU::PixelCount; i++) {
_ppuOutputBuffer[i] = 14; //Black
}
DecodeFrame();
}*/
_ppuOutputBuffer = nullptr;
}
#endif
}
bool VideoDecoder::IsRunning()
{
return _decodeThread != nullptr;
}
void VideoDecoder::TakeScreenshot()
{
if(_videoFilter) {
//TODO
//_videoFilter->TakeScreenshot(_console->GetRomPath().GetFileName(), _videoFilterType);
}
}
void VideoDecoder::TakeScreenshot(std::stringstream &stream)
{
if(_videoFilter) {
_videoFilter->TakeScreenshot(_videoFilterType, "", &stream);
}
}

63
Core/VideoDecoder.h Normal file
View file

@ -0,0 +1,63 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/SimpleLock.h"
#include "../Utilities/AutoResetEvent.h"
#include "SettingTypes.h"
class BaseVideoFilter;
//class ScaleFilter;
//class RotateFilter;
class IRenderingDevice;
//class VideoHud;
class Console;
class VideoDecoder
{
private:
shared_ptr<Console> _console;
uint16_t *_ppuOutputBuffer = nullptr;
uint32_t _frameNumber = 0;
unique_ptr<thread> _decodeThread;
//unique_ptr<VideoHud> _hud;
AutoResetEvent _waitForFrame;
atomic<bool> _frameChanged;
atomic<bool> _stopFlag;
uint32_t _frameCount = 0;
ScreenSize _previousScreenSize = {};
double _previousScale = 0;
FrameInfo _lastFrameInfo;
VideoFilterType _videoFilterType = VideoFilterType::None;
unique_ptr<BaseVideoFilter> _videoFilter;
//shared_ptr<ScaleFilter> _scaleFilter;
//shared_ptr<RotateFilter> _rotateFilter;
void UpdateVideoFilter();
void DecodeThread();
public:
VideoDecoder(shared_ptr<Console> console);
~VideoDecoder();
void DecodeFrame(bool synchronous = false);
void TakeScreenshot();
void TakeScreenshot(std::stringstream &stream);
uint32_t GetFrameCount();
FrameInfo GetFrameInfo();
void GetScreenSize(ScreenSize &size, bool ignoreScale);
void UpdateFrameSync(uint16_t *ppuOutputBuffer, uint32_t frameNumber);
void UpdateFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber);
bool IsRunning();
void StartThread();
void StopThread();
};

116
Core/VideoRenderer.cpp Normal file
View file

@ -0,0 +1,116 @@
#include "stdafx.h"
#include "IRenderingDevice.h"
#include "VideoRenderer.h"
//#include "AviRecorder.h"
#include "VideoDecoder.h"
#include "Console.h"
VideoRenderer::VideoRenderer(shared_ptr<Console> console)
{
_console = console;
_stopFlag = false;
StartThread();
}
VideoRenderer::~VideoRenderer()
{
_stopFlag = true;
StopThread();
}
void VideoRenderer::StartThread()
{
#ifndef LIBRETRO
if(!_renderThread) {
_stopFlag = false;
_waitForRender.Reset();
_renderThread.reset(new std::thread(&VideoRenderer::RenderThread, this));
}
#endif
}
void VideoRenderer::StopThread()
{
#ifndef LIBRETRO
_stopFlag = true;
if(_renderThread) {
_renderThread->join();
_renderThread.reset();
}
#endif
}
void VideoRenderer::RenderThread()
{
if(_renderer) {
_renderer->Reset();
}
while(!_stopFlag.load()) {
//Wait until a frame is ready, or until 16ms have passed (to allow UI to run at a minimum of 60fps)
_waitForRender.Wait(16);
if(_renderer) {
_renderer->Render();
}
}
}
void VideoRenderer::UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height)
{
//TODO
/*shared_ptr<AviRecorder> aviRecorder = _aviRecorder;
if(aviRecorder) {
aviRecorder->AddFrame(frameBuffer, width, height);
}*/
if(_renderer) {
_renderer->UpdateFrame(frameBuffer, width, height);
_waitForRender.Signal();
}
}
void VideoRenderer::RegisterRenderingDevice(IRenderingDevice *renderer)
{
_renderer = renderer;
StartThread();
}
void VideoRenderer::UnregisterRenderingDevice(IRenderingDevice *renderer)
{
if(_renderer == renderer) {
StopThread();
_renderer = nullptr;
}
}
//TODO
/*
void VideoRenderer::StartRecording(string filename, VideoCodec codec, uint32_t compressionLevel)
{
shared_ptr<AviRecorder> recorder(new AviRecorder(_console));
FrameInfo frameInfo = _console->GetVideoDecoder()->GetFrameInfo();
if(recorder->StartRecording(filename, codec, frameInfo.Width, frameInfo.Height, frameInfo.BitsPerPixel, _console->GetSettings()->GetSampleRate(), compressionLevel)) {
_aviRecorder = recorder;
}
}
void VideoRenderer::AddRecordingSound(int16_t* soundBuffer, uint32_t sampleCount, uint32_t sampleRate)
{
shared_ptr<AviRecorder> aviRecorder = _aviRecorder;
if(aviRecorder) {
aviRecorder->AddSound(soundBuffer, sampleCount, sampleRate);
}
}
void VideoRenderer::StopRecording()
{
_aviRecorder.reset();
}
bool VideoRenderer::IsRecording()
{
return _aviRecorder != nullptr && _aviRecorder->IsRecording();
}
*/

45
Core/VideoRenderer.h Normal file
View file

@ -0,0 +1,45 @@
#pragma once
#include "stdafx.h"
#include <thread>
#include "../Utilities/AutoResetEvent.h"
class IRenderingDevice;
class Console;
//TODO
//class AviRecorder;
//enum class VideoCodec;
class VideoRenderer
{
private:
shared_ptr<Console> _console;
AutoResetEvent _waitForRender;
unique_ptr<std::thread> _renderThread;
IRenderingDevice* _renderer = nullptr;
atomic<bool> _stopFlag;
//TODO
//shared_ptr<AviRecorder> _aviRecorder;
void RenderThread();
public:
VideoRenderer(shared_ptr<Console> console);
~VideoRenderer();
void StartThread();
void StopThread();
void UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height);
void RegisterRenderingDevice(IRenderingDevice *renderer);
void UnregisterRenderingDevice(IRenderingDevice *renderer);
//TODO
/*
void StartRecording(string filename, VideoCodec codec, uint32_t compressionLevel);
void AddRecordingSound(int16_t* soundBuffer, uint32_t sampleCount, uint32_t sampleRate);
void StopRecording();
bool IsRecording();*/
};

View file

@ -16,6 +16,7 @@
#include <sstream>
#include <list>
#include <atomic>
#include <thread>
#include "../Utilities/UTF8Util.h"
@ -42,6 +43,7 @@ using std::max;
using std::string;
using std::atomic_flag;
using std::atomic;
using std::thread;
#ifdef _DEBUG
#pragma comment(lib, "C:\\Code\\Mesen-S\\bin\\x64\\Debug\\Utilities.lib")

View file

@ -62,6 +62,7 @@ namespace InteropEmu {
DllExport void __stdcall InitDll()
{
_console.reset(new Console());
_console->Initialize();
}
DllExport void __stdcall InitializeEmu(const char* homeFolder, void *windowHandle, void *viewerHandle, bool noAudio, bool noVideo, bool noInput)

View file

@ -34,10 +34,10 @@
this.debugToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.mnuRun = new System.Windows.Forms.ToolStripMenuItem();
this.mnuStep = new System.Windows.Forms.ToolStripMenuItem();
this.mnuRun100Instructions = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
this.mnuDebugger = new System.Windows.Forms.ToolStripMenuItem();
this.mnuTraceLogger = new System.Windows.Forms.ToolStripMenuItem();
this.mnuRun100Instructions = new System.Windows.Forms.ToolStripMenuItem();
this.mnuMain.SuspendLayout();
this.SuspendLayout();
//
@ -55,7 +55,7 @@
this.debugToolStripMenuItem});
this.mnuMain.Location = new System.Drawing.Point(0, 0);
this.mnuMain.Name = "mnuMain";
this.mnuMain.Size = new System.Drawing.Size(514, 24);
this.mnuMain.Size = new System.Drawing.Size(512, 24);
this.mnuMain.TabIndex = 1;
this.mnuMain.Text = "ctrlMesenMenuStrip1";
//
@ -71,7 +71,8 @@
//
this.mnuOpen.Image = global::Mesen.GUI.Properties.Resources.Folder;
this.mnuOpen.Name = "mnuOpen";
this.mnuOpen.Size = new System.Drawing.Size(103, 22);
this.mnuOpen.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O)));
this.mnuOpen.Size = new System.Drawing.Size(152, 22);
this.mnuOpen.Text = "Open";
this.mnuOpen.Click += new System.EventHandler(this.mnuOpen_Click);
//
@ -92,7 +93,7 @@
//
this.mnuRun.Name = "mnuRun";
this.mnuRun.ShortcutKeys = System.Windows.Forms.Keys.F5;
this.mnuRun.Size = new System.Drawing.Size(157, 22);
this.mnuRun.Size = new System.Drawing.Size(163, 22);
this.mnuRun.Text = "Run";
this.mnuRun.Click += new System.EventHandler(this.mnuRun_Click);
//
@ -100,41 +101,41 @@
//
this.mnuStep.Name = "mnuStep";
this.mnuStep.ShortcutKeys = System.Windows.Forms.Keys.F11;
this.mnuStep.Size = new System.Drawing.Size(157, 22);
this.mnuStep.Size = new System.Drawing.Size(163, 22);
this.mnuStep.Text = "Step";
this.mnuStep.Click += new System.EventHandler(this.mnuStep_Click);
//
// toolStripMenuItem1
//
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(154, 6);
//
// mnuDebugger
//
this.mnuDebugger.Name = "mnuDebugger";
this.mnuDebugger.Size = new System.Drawing.Size(157, 22);
this.mnuDebugger.Text = "Debugger";
//
// mnuTraceLogger
//
this.mnuTraceLogger.Name = "mnuTraceLogger";
this.mnuTraceLogger.Size = new System.Drawing.Size(157, 22);
this.mnuTraceLogger.Text = "Trace Logger";
this.mnuTraceLogger.Click += new System.EventHandler(this.mnuTraceLogger_Click);
//
// mnuRun100Instructions
//
this.mnuRun100Instructions.Name = "mnuRun100Instructions";
this.mnuRun100Instructions.ShortcutKeys = System.Windows.Forms.Keys.F6;
this.mnuRun100Instructions.Size = new System.Drawing.Size(157, 22);
this.mnuRun100Instructions.Text = "Run 100 ops";
this.mnuRun100Instructions.Size = new System.Drawing.Size(163, 22);
this.mnuRun100Instructions.Text = "Run 1000 ops";
this.mnuRun100Instructions.Click += new System.EventHandler(this.mnuRun100Instructions_Click);
//
// toolStripMenuItem1
//
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(160, 6);
//
// mnuDebugger
//
this.mnuDebugger.Name = "mnuDebugger";
this.mnuDebugger.Size = new System.Drawing.Size(163, 22);
this.mnuDebugger.Text = "Debugger";
//
// mnuTraceLogger
//
this.mnuTraceLogger.Name = "mnuTraceLogger";
this.mnuTraceLogger.Size = new System.Drawing.Size(163, 22);
this.mnuTraceLogger.Text = "Trace Logger";
this.mnuTraceLogger.Click += new System.EventHandler(this.mnuTraceLogger_Click);
//
// frmMain
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(514, 476);
this.ClientSize = new System.Drawing.Size(512, 475);
this.Controls.Add(this.ctrlRenderer);
this.Controls.Add(this.mnuMain);
this.MainMenuStrip = this.mnuMain;

View file

@ -30,7 +30,8 @@ namespace Mesen.GUI.Forms
private void mnuTraceLogger_Click(object sender, EventArgs e)
{
frmTraceLogger frm = new frmTraceLogger();
frm.Show();
}
private void mnuStep_Click(object sender, EventArgs e)
@ -47,9 +48,6 @@ namespace Mesen.GUI.Forms
Task.Run(() => {
EmuApi.Run();
});
frmTraceLogger frm = new frmTraceLogger();
frm.Show();
}
}
}
@ -61,7 +59,7 @@ namespace Mesen.GUI.Forms
private void mnuRun100Instructions_Click(object sender, EventArgs e)
{
DebugApi.Step(100);
DebugApi.Step(1000);
}
}
}

View file

@ -3,8 +3,11 @@
#include "DirectXTK/SpriteBatch.h"
#include "DirectXTK/SpriteFont.h"
#include "../Core/Console.h"
#include "../Core/VideoDecoder.h"
#include "../Core/VideoRenderer.h"
#include "../Core/Debugger.h"
#include "../Core/MessageManager.h"
#include "../Core/SettingTypes.h"
#include "../Utilities/UTF8Util.h"
using namespace DirectX;
@ -13,16 +16,15 @@ Renderer::Renderer(shared_ptr<Console> console, HWND hWnd, bool registerAsMessag
{
_hWnd = hWnd;
SetScreenSize(256, 240);
SetScreenSize(256, 224);
}
Renderer::~Renderer()
{
//TODO
/*shared_ptr<VideoRenderer> videoRenderer = _console->GetVideoRenderer();
shared_ptr<VideoRenderer> videoRenderer = _console->GetVideoRenderer();
if(videoRenderer) {
videoRenderer->UnregisterRenderingDevice(this);
}*/
}
CleanupDevice();
}
@ -38,20 +40,20 @@ void Renderer::SetFullscreenMode(bool fullscreen, void* windowHandle, uint32_t m
void Renderer::SetScreenSize(uint32_t width, uint32_t height)
{
//TODO
/*ScreenSize screenSize;
ScreenSize screenSize;
_console->GetVideoDecoder()->GetScreenSize(screenSize, false);
if(_screenHeight != screenSize.Height || _screenWidth != screenSize.Width || _nesFrameHeight != height || _nesFrameWidth != width || _resizeFilter != _console->GetSettings()->GetVideoResizeFilter() || _newFullscreen != _fullscreen) {
//TODO _resizeFilter != _console->GetSettings()->GetVideoResizeFilter()
if(_screenHeight != screenSize.Height || _screenWidth != screenSize.Width || _nesFrameHeight != height || _nesFrameWidth != width || _newFullscreen != _fullscreen) {
auto frameLock = _frameLock.AcquireSafe();
auto textureLock = _textureLock.AcquireSafe();
_console->GetVideoDecoder()->GetScreenSize(screenSize, false);
if(_screenHeight != screenSize.Height || _screenWidth != screenSize.Width || _nesFrameHeight != height || _nesFrameWidth != width || _resizeFilter != _console->GetSettings()->GetVideoResizeFilter() || _newFullscreen != _fullscreen) {
if(_screenHeight != screenSize.Height || _screenWidth != screenSize.Width || _nesFrameHeight != height || _nesFrameWidth != width || _newFullscreen != _fullscreen) {
_nesFrameHeight = height;
_nesFrameWidth = width;
_newFrameBufferSize = width*height;
bool needReset = _fullscreen != _newFullscreen || _resizeFilter != _console->GetSettings()->GetVideoResizeFilter();
bool needReset = _fullscreen != _newFullscreen;//TODO || _resizeFilter != _console->GetSettings()->GetVideoResizeFilter();
bool fullscreenResizeMode = _fullscreen && _newFullscreen;
if(_pSwapChain && _fullscreen && !_newFullscreen) {
@ -94,7 +96,7 @@ void Renderer::SetScreenSize(uint32_t width, uint32_t height)
}
}
}
}*/
}
}
void Renderer::Reset()
@ -104,8 +106,7 @@ void Renderer::Reset()
if(FAILED(InitDevice())) {
CleanupDevice();
} else {
//TODO
//_console->GetVideoRenderer()->RegisterRenderingDevice(this);
_console->GetVideoRenderer()->RegisterRenderingDevice(this);
}
}