2015-07-23 23:16:31 -04:00
|
|
|
#include "stdafx.h"
|
2015-08-30 21:04:21 -04:00
|
|
|
#include "IRenderingDevice.h"
|
2015-07-23 23:16:31 -04:00
|
|
|
#include "VideoDecoder.h"
|
|
|
|
#include "EmulationSettings.h"
|
2016-01-05 21:28:38 -05:00
|
|
|
#include "DefaultVideoFilter.h"
|
2016-12-27 15:04:20 -05:00
|
|
|
#include "BisqwitNtscFilter.h"
|
2016-01-05 21:28:38 -05:00
|
|
|
#include "NtscFilter.h"
|
|
|
|
#include "HdVideoFilter.h"
|
2016-05-23 17:02:24 -04:00
|
|
|
#include "ScaleFilter.h"
|
2016-01-31 00:41:33 -05:00
|
|
|
#include "VideoRenderer.h"
|
2017-04-28 19:54:58 -04:00
|
|
|
#include "RewindManager.h"
|
2017-04-29 08:29:56 -04:00
|
|
|
#include "PPU.h"
|
2017-07-25 19:46:25 -04:00
|
|
|
#include "HdNesPack.h"
|
2017-11-11 20:55:33 -05:00
|
|
|
#include "RotateFilter.h"
|
2015-07-23 23:16:31 -04:00
|
|
|
|
|
|
|
unique_ptr<VideoDecoder> VideoDecoder::Instance;
|
|
|
|
|
|
|
|
VideoDecoder* VideoDecoder::GetInstance()
|
|
|
|
{
|
|
|
|
if(!Instance) {
|
|
|
|
Instance.reset(new VideoDecoder());
|
|
|
|
}
|
|
|
|
return Instance.get();
|
|
|
|
}
|
|
|
|
|
2016-12-27 15:04:20 -05:00
|
|
|
void VideoDecoder::Release()
|
|
|
|
{
|
|
|
|
if(Instance) {
|
|
|
|
Instance.reset();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-30 21:04:21 -04:00
|
|
|
VideoDecoder::VideoDecoder()
|
|
|
|
{
|
2016-12-11 10:56:23 -05:00
|
|
|
_frameChanged = false;
|
|
|
|
_stopFlag = false;
|
2016-01-05 21:28:38 -05:00
|
|
|
UpdateVideoFilter();
|
2015-08-30 21:04:21 -04:00
|
|
|
}
|
|
|
|
|
2015-07-23 23:16:31 -04:00
|
|
|
VideoDecoder::~VideoDecoder()
|
|
|
|
{
|
2015-08-30 21:04:21 -04:00
|
|
|
StopThread();
|
2015-07-23 23:16:31 -04:00
|
|
|
}
|
|
|
|
|
2017-04-28 19:54:58 -04:00
|
|
|
FrameInfo VideoDecoder::GetFrameInfo()
|
|
|
|
{
|
2017-11-11 20:55:33 -05:00
|
|
|
return _lastFrameInfo;
|
2017-04-28 19:54:58 -04:00
|
|
|
}
|
|
|
|
|
2016-02-07 13:05:32 -05:00
|
|
|
void VideoDecoder::GetScreenSize(ScreenSize &size, bool ignoreScale)
|
2015-07-23 23:16:31 -04:00
|
|
|
{
|
2016-01-05 21:28:38 -05:00
|
|
|
if(_videoFilter) {
|
2017-09-17 16:04:26 -04:00
|
|
|
OverscanDimensions overscan = ignoreScale ? _videoFilter->GetOverscan() : EmulationSettings::GetOverscanDimensions();
|
2017-05-01 23:05:31 -04:00
|
|
|
FrameInfo frameInfo{ overscan.GetScreenWidth(), overscan.GetScreenHeight(), PPU::ScreenWidth, PPU::ScreenHeight, 4 };
|
2016-02-07 13:05:32 -05:00
|
|
|
double aspectRatio = EmulationSettings::GetAspectRatio();
|
2016-12-09 09:23:04 -05:00
|
|
|
double scale = (ignoreScale ? 1 : EmulationSettings::GetVideoScale());
|
|
|
|
size.Width = (int32_t)(frameInfo.Width * scale);
|
|
|
|
size.Height = (int32_t)(frameInfo.Height * scale);
|
2016-02-07 13:05:32 -05:00
|
|
|
if(aspectRatio != 0.0) {
|
2016-12-09 09:23:04 -05:00
|
|
|
size.Width = (uint32_t)(frameInfo.OriginalHeight * scale * aspectRatio * ((double)frameInfo.Width / frameInfo.OriginalWidth));
|
2016-02-07 13:05:32 -05:00
|
|
|
}
|
2017-11-11 20:55:33 -05:00
|
|
|
|
|
|
|
if(EmulationSettings::GetScreenRotation() % 180) {
|
|
|
|
std::swap(size.Width, size.Height);
|
|
|
|
}
|
|
|
|
|
2016-12-22 23:08:34 -05:00
|
|
|
size.Scale = scale;
|
2015-07-23 23:16:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-05 21:28:38 -05:00
|
|
|
void VideoDecoder::UpdateVideoFilter()
|
2015-07-23 23:16:31 -04:00
|
|
|
{
|
2016-01-05 21:28:38 -05:00
|
|
|
VideoFilterType newFilter = EmulationSettings::GetVideoFilterType();
|
|
|
|
|
2017-08-15 23:59:55 -04:00
|
|
|
if(_videoFilterType != newFilter || _videoFilter == nullptr || (_hdScreenTiles && !_hdFilterEnabled) || (!_hdScreenTiles && _hdFilterEnabled)) {
|
2016-01-05 21:28:38 -05:00
|
|
|
_videoFilterType = newFilter;
|
2017-08-15 23:59:55 -04:00
|
|
|
_videoFilter.reset(new DefaultVideoFilter());
|
|
|
|
_scaleFilter.reset();
|
2015-07-23 23:16:31 -04:00
|
|
|
|
2016-01-05 21:28:38 -05:00
|
|
|
switch(_videoFilterType) {
|
2017-09-01 16:00:55 -04:00
|
|
|
case VideoFilterType::None: break;
|
2016-01-05 21:28:38 -05:00
|
|
|
case VideoFilterType::NTSC: _videoFilter.reset(new NtscFilter()); break;
|
2016-12-27 15:04:20 -05:00
|
|
|
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;
|
2017-09-01 16:00:55 -04:00
|
|
|
default: _scaleFilter = ScaleFilter::GetScaleFilter(_videoFilterType); break;
|
2017-08-15 23:59:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
_hdFilterEnabled = false;
|
|
|
|
if(_hdScreenTiles) {
|
|
|
|
_videoFilter.reset(new HdVideoFilter());
|
|
|
|
_hdFilterEnabled = true;
|
2015-07-23 23:16:31 -04:00
|
|
|
}
|
|
|
|
}
|
2017-11-11 20:55:33 -05:00
|
|
|
|
|
|
|
if(EmulationSettings::GetScreenRotation() == 0 && _rotateFilter) {
|
|
|
|
_rotateFilter.reset();
|
|
|
|
} else if(EmulationSettings::GetScreenRotation() > 0) {
|
|
|
|
if(!_rotateFilter || _rotateFilter->GetAngle() != EmulationSettings::GetScreenRotation()) {
|
|
|
|
_rotateFilter.reset(new RotateFilter(EmulationSettings::GetScreenRotation()));
|
|
|
|
}
|
|
|
|
}
|
2015-07-23 23:16:31 -04:00
|
|
|
}
|
|
|
|
|
2017-11-26 18:19:37 -05:00
|
|
|
void VideoDecoder::DecodeFrame(bool synchronous)
|
2015-08-14 21:50:14 -04:00
|
|
|
{
|
2016-01-05 21:28:38 -05:00
|
|
|
UpdateVideoFilter();
|
2015-08-30 21:04:21 -04:00
|
|
|
|
2017-08-15 23:59:55 -04:00
|
|
|
if(_hdFilterEnabled) {
|
2016-01-05 21:28:38 -05:00
|
|
|
((HdVideoFilter*)_videoFilter.get())->SetHdScreenTiles(_hdScreenTiles);
|
2015-08-14 21:50:14 -04:00
|
|
|
}
|
2018-02-16 18:16:06 -05:00
|
|
|
_videoFilter->SendFrame(_ppuOutputBuffer, _isOddFrame);
|
2016-01-05 21:28:38 -05:00
|
|
|
|
2017-08-15 23:59:55 -04:00
|
|
|
uint32_t* outputBuffer = (uint32_t*)_videoFilter->GetOutputBuffer();
|
2017-11-11 20:55:33 -05:00
|
|
|
FrameInfo frameInfo = _videoFilter->GetFrameInfo();
|
|
|
|
|
|
|
|
if(_rotateFilter) {
|
|
|
|
outputBuffer = _rotateFilter->ApplyFilter(outputBuffer, frameInfo.Width, frameInfo.Height);
|
|
|
|
frameInfo = _rotateFilter->GetFrameInfo(frameInfo);
|
|
|
|
}
|
|
|
|
|
2017-08-15 23:59:55 -04:00
|
|
|
if(_scaleFilter) {
|
2017-11-11 20:55:33 -05:00
|
|
|
outputBuffer = _scaleFilter->ApplyFilter(outputBuffer, frameInfo.Width, frameInfo.Height);
|
|
|
|
frameInfo = _scaleFilter->GetFrameInfo(frameInfo);
|
2017-08-15 23:59:55 -04:00
|
|
|
}
|
|
|
|
|
2017-11-28 22:44:06 -05:00
|
|
|
if(_hud) {
|
|
|
|
_hud->DrawHud((uint8_t*)outputBuffer, frameInfo, _videoFilter->GetOverscan());
|
|
|
|
}
|
2017-11-11 20:55:33 -05:00
|
|
|
|
2016-12-30 16:43:49 -05:00
|
|
|
ScreenSize screenSize;
|
|
|
|
GetScreenSize(screenSize, true);
|
|
|
|
if(_previousScale != EmulationSettings::GetVideoScale() || screenSize.Height != _previousScreenSize.Height || screenSize.Width != _previousScreenSize.Width) {
|
2016-12-22 23:08:34 -05:00
|
|
|
MessageManager::SendNotification(ConsoleNotificationType::ResolutionChanged);
|
|
|
|
}
|
|
|
|
_previousScale = EmulationSettings::GetVideoScale();
|
2016-12-30 16:43:49 -05:00
|
|
|
_previousScreenSize = screenSize;
|
2016-12-29 21:19:13 -05:00
|
|
|
|
2017-11-11 20:55:33 -05:00
|
|
|
_lastFrameInfo = frameInfo;
|
2015-08-14 21:50:14 -04:00
|
|
|
|
2016-12-30 16:43:49 -05:00
|
|
|
_frameChanged = false;
|
|
|
|
|
2017-04-28 19:54:58 -04:00
|
|
|
//Rewind manager will take care of sending the correct frame to the video renderer
|
2017-11-26 18:19:37 -05:00
|
|
|
RewindManager::SendFrame(outputBuffer, frameInfo.Width, frameInfo.Height, synchronous);
|
2015-08-14 21:50:14 -04:00
|
|
|
}
|
|
|
|
|
2015-08-30 21:04:21 -04:00
|
|
|
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
|
2016-12-29 21:19:13 -05:00
|
|
|
while(!_frameChanged) {
|
2015-08-30 21:04:21 -04:00
|
|
|
_waitForFrame.Wait();
|
|
|
|
if(_stopFlag.load()) {
|
2016-01-12 19:42:28 -05:00
|
|
|
return;
|
2015-08-30 21:04:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DecodeFrame();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t VideoDecoder::GetFrameCount()
|
|
|
|
{
|
|
|
|
return _frameCount;
|
|
|
|
}
|
|
|
|
|
2017-04-28 19:54:58 -04:00
|
|
|
void VideoDecoder::UpdateFrameSync(void *ppuOutputBuffer, HdPpuPixelInfo *hdPixelInfo)
|
|
|
|
{
|
2018-02-16 18:16:06 -05:00
|
|
|
_isOddFrame = (PPU::GetFrameCount() & 0x01) == 0x01;
|
2017-04-28 19:54:58 -04:00
|
|
|
_hdScreenTiles = hdPixelInfo;
|
|
|
|
_ppuOutputBuffer = (uint16_t*)ppuOutputBuffer;
|
2017-11-26 18:19:37 -05:00
|
|
|
DecodeFrame(true);
|
2017-04-28 19:54:58 -04:00
|
|
|
_frameCount++;
|
|
|
|
}
|
|
|
|
|
2016-01-12 19:42:28 -05:00
|
|
|
void VideoDecoder::UpdateFrame(void *ppuOutputBuffer, HdPpuPixelInfo *hdPixelInfo)
|
2015-08-30 21:04:21 -04:00
|
|
|
{
|
2016-01-12 19:42:28 -05:00
|
|
|
if(_frameChanged) {
|
|
|
|
//Last frame isn't done decoding yet - sometimes Signal() introduces a 25-30ms delay
|
2017-04-28 19:54:58 -04:00
|
|
|
while(_frameChanged) {
|
2016-01-12 19:42:28 -05:00
|
|
|
//Spin until decode is done
|
|
|
|
}
|
|
|
|
//At this point, we are sure that the decode thread is no longer busy
|
2015-08-30 21:04:21 -04:00
|
|
|
}
|
2018-02-16 18:16:06 -05:00
|
|
|
|
|
|
|
_isOddFrame = (PPU::GetFrameCount() & 0x01) == 0x01;
|
2016-01-12 19:42:28 -05:00
|
|
|
_hdScreenTiles = hdPixelInfo;
|
|
|
|
_ppuOutputBuffer = (uint16_t*)ppuOutputBuffer;
|
|
|
|
_frameChanged = true;
|
|
|
|
_waitForFrame.Signal();
|
|
|
|
|
|
|
|
_frameCount++;
|
2015-08-30 21:04:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void VideoDecoder::StartThread()
|
|
|
|
{
|
2018-01-04 19:03:47 -05:00
|
|
|
#ifndef LIBRETRO
|
2016-01-16 12:29:17 -05:00
|
|
|
if(!_decodeThread) {
|
2015-08-30 21:04:21 -04:00
|
|
|
_stopFlag = false;
|
2015-12-26 17:11:00 -05:00
|
|
|
_frameChanged = false;
|
|
|
|
_frameCount = 0;
|
2016-01-06 20:37:52 -05:00
|
|
|
_waitForFrame.Reset();
|
2017-11-28 22:44:06 -05:00
|
|
|
_hud.reset(new VideoHud());
|
2016-01-16 12:29:17 -05:00
|
|
|
_decodeThread.reset(new thread(&VideoDecoder::DecodeThread, this));
|
2015-08-30 21:04:21 -04:00
|
|
|
}
|
2018-01-04 19:03:47 -05:00
|
|
|
#endif
|
2015-08-30 21:04:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void VideoDecoder::StopThread()
|
|
|
|
{
|
2018-01-04 19:03:47 -05:00
|
|
|
#ifndef LIBRETRO
|
2015-08-30 21:04:21 -04:00
|
|
|
_stopFlag = true;
|
|
|
|
if(_decodeThread) {
|
|
|
|
_waitForFrame.Signal();
|
|
|
|
_decodeThread->join();
|
2015-12-26 17:11:00 -05:00
|
|
|
|
2016-06-17 20:54:11 -04:00
|
|
|
_decodeThread.reset();
|
2015-08-30 21:04:21 -04:00
|
|
|
|
2017-11-28 22:44:06 -05:00
|
|
|
_hud.reset();
|
2017-05-06 15:27:48 -04:00
|
|
|
_hdScreenTiles = nullptr;
|
2017-05-22 14:52:18 -04:00
|
|
|
EmulationSettings::SetPpuModel(PpuModel::Ppu2C02);
|
2017-05-06 15:27:48 -04:00
|
|
|
UpdateVideoFilter();
|
2016-01-31 00:41:33 -05:00
|
|
|
if(_ppuOutputBuffer != nullptr) {
|
|
|
|
//Clear whole screen
|
2016-12-11 10:56:23 -05:00
|
|
|
for(uint32_t i = 0; i < PPU::PixelCount; i++) {
|
2016-01-31 00:41:33 -05:00
|
|
|
_ppuOutputBuffer[i] = 14; //Black
|
|
|
|
}
|
|
|
|
DecodeFrame();
|
2015-08-30 21:04:21 -04:00
|
|
|
}
|
2016-02-13 23:17:59 -05:00
|
|
|
_ppuOutputBuffer = nullptr;
|
2015-08-30 21:04:21 -04:00
|
|
|
}
|
2018-01-04 19:03:47 -05:00
|
|
|
#endif
|
2015-08-30 21:04:21 -04:00
|
|
|
}
|
|
|
|
|
2016-01-31 00:41:33 -05:00
|
|
|
bool VideoDecoder::IsRunning()
|
2015-08-30 21:04:21 -04:00
|
|
|
{
|
2016-01-31 00:41:33 -05:00
|
|
|
return _decodeThread != nullptr;
|
2016-01-05 21:28:38 -05:00
|
|
|
}
|
|
|
|
|
2016-06-17 20:53:05 -04:00
|
|
|
void VideoDecoder::TakeScreenshot()
|
2016-01-05 21:28:38 -05:00
|
|
|
{
|
|
|
|
if(_videoFilter) {
|
2017-09-01 16:00:55 -04:00
|
|
|
_videoFilter->TakeScreenshot(_videoFilterType);
|
2016-01-05 21:28:38 -05:00
|
|
|
}
|
2016-12-29 21:19:13 -05:00
|
|
|
}
|
2017-05-06 15:27:48 -04:00
|
|
|
|
|
|
|
void VideoDecoder::TakeScreenshot(std::stringstream &stream)
|
|
|
|
{
|
|
|
|
if(_videoFilter) {
|
2017-09-01 16:00:55 -04:00
|
|
|
_videoFilter->TakeScreenshot(_videoFilterType, "", &stream);
|
2017-05-06 15:27:48 -04:00
|
|
|
}
|
|
|
|
}
|