Skip to content

Commit

Permalink
Correct dot crawl phase offset calculation
Browse files Browse the repository at this point in the history
- Thanks lidnariq!
- The new formula is based on the color generator clock cycle count per
  frame.
  • Loading branch information
Gumball2415 committed Oct 22, 2023
1 parent 3b72d78 commit fa7c32f
Show file tree
Hide file tree
Showing 8 changed files with 29 additions and 19 deletions.
1 change: 1 addition & 0 deletions Core/NES/BaseNesPpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class BaseNesPpu : public INesMemoryHandler, public ISerializable
{
protected:
uint64_t _masterClock = 0;
uint64_t _masterClockFrameStart = 0;
uint32_t _cycle = 0;
int16_t _scanline = 0;
bool _emulatorBgEnabled = false;
Expand Down
4 changes: 2 additions & 2 deletions Core/NES/BisqwitNtscFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ BisqwitNtscFilter::BisqwitNtscFilter(Emulator* emu) : BaseVideoFilter(emu)
//Adjust outputbuffer to start at the middle of the picture
outputBuffer += frameInfo.Width * (frameInfo.Height / 2);

DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer, (GetVideoPhase() * 4) + 327360);
DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer, GetVideoPhaseOffset() + 120*341*_signalsPerPixel);

_workDone = true;
}
Expand All @@ -89,7 +89,7 @@ void BisqwitNtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer)

_workDone = false;
_waitWork.Signal();
DecodeFrame(GetOverscan().Top, 120, ppuOutputBuffer, GetOutputBuffer(), (GetVideoPhase() * 4) + GetOverscan().Top*341*8);
DecodeFrame(GetOverscan().Top, 120, ppuOutputBuffer, GetOutputBuffer(), GetVideoPhaseOffset() + GetOverscan().Top * 341 * _signalsPerPixel);
while(!_workDone) {}
}

Expand Down
2 changes: 1 addition & 1 deletion Core/NES/NesNtscFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ void NesNtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
NesDefaultVideoFilter::ApplyPalBorder(ppuOutputBuffer);
}

nes_ntsc_blit(&_ntscData, ppuOutputBuffer, _baseFrameInfo.Width, GetVideoPhase(), _baseFrameInfo.Width, _baseFrameInfo.Height, _ntscBuffer, NES_NTSC_OUT_WIDTH(_baseFrameInfo.Width) * 4);
nes_ntsc_blit(&_ntscData, ppuOutputBuffer, _baseFrameInfo.Width, (GetVideoPhaseOffset() / 4), _baseFrameInfo.Width, _baseFrameInfo.Height, _ntscBuffer, NES_NTSC_OUT_WIDTH(_baseFrameInfo.Width) * 4);

for(uint32_t i = 0; i < frameInfo.Height; i+=2) {
memcpy(GetOutputBuffer()+i*frameInfo.Width, _ntscBuffer + yOffset + xOffset + (i/2)*baseWidth, frameInfo.Width * sizeof(uint32_t));
Expand Down
19 changes: 14 additions & 5 deletions Core/NES/NesPpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ template<class T> NesPpu<T>::NesPpu(NesConsole* console)
_emu = console->GetEmulator();
_mapper = console->GetMapper();
_masterClock = 0;
_masterClockFrameStart = 0;
_masterClockDivider = 4;
_settings = _emu->GetSettings();

Expand Down Expand Up @@ -78,6 +79,7 @@ template<class T> NesPpu<T>::NesPpu(NesConsole* console)
template<class T> void NesPpu<T>::Reset(bool softReset)
{
_masterClock = 0;
_masterClockFrameStart = 0;

//Reset OAM decay timestamps regardless of the reset PPU option
memset(_oamDecayCycles, 0, sizeof(_oamDecayCycles));
Expand Down Expand Up @@ -868,6 +870,10 @@ template<class T> void NesPpu<T>::ProcessScanlineImpl()
}

if(_scanline >= 0) {
if(_scanline == 0 && _cycle == 1) {
// get the master clock at the first dot
_masterClockFrameStart = _masterClock;
}
((T*)this)->DrawPixel();
ShiftTileRegisters();

Expand Down Expand Up @@ -1104,15 +1110,18 @@ template<class T> void NesPpu<T>::SendFrame()
_emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone, _currentOutputBuffer);
}

//Get phase at the start of the current frame (341*241 cycles ago)
uint32_t videoPhase = ((_masterClock / _masterClockDivider) - 82181) % 3;
//Get chroma phase offset at the start of the current frame
//In units of color generator clocks
//https://www.nesdev.org/wiki/NTSC_video#Color_Artifacts
uint32_t videoPhaseOffset = (_masterClockFrameStart % 6) * 2;

NesConfig& cfg = _console->GetNesConfig();
if(_region != ConsoleRegion::Ntsc || cfg.PpuExtraScanlinesAfterNmi != 0 || cfg.PpuExtraScanlinesBeforeNmi != 0) {
//Force 2-phase pattern for PAL or when overclocking is used
videoPhase = _frameCount & 0x01;
videoPhaseOffset = (_frameCount & 0x01) * 4;
}

RenderedFrame frame(_currentOutputBuffer, NesConstants::ScreenWidth, NesConstants::ScreenHeight, 1.0, _frameCount, _console->GetControlManager()->GetPortStates(), videoPhase);
RenderedFrame frame(_currentOutputBuffer, NesConstants::ScreenWidth, NesConstants::ScreenHeight, 1.0, _frameCount, _console->GetControlManager()->GetPortStates(), videoPhaseOffset);
frame.Data = frameData; //HD packs

if(_console->GetVsMainConsole() || _console->GetVsSubConsole()) {
Expand Down Expand Up @@ -1462,7 +1471,7 @@ template<class T> void NesPpu<T>::Serialize(Serializer& s)
SV(_mask.IntensifyBlue); SV(_paletteRamMask); SV(_intensifyColorBits); SV(_statusFlags.SpriteOverflow); SV(_statusFlags.Sprite0Hit); SV(_statusFlags.VerticalBlank); SV(_scanline);
SV(_cycle); SV(_frameCount); SV(_memoryReadBuffer); SV(_region);

SV(_ppuBusAddress); SV(_masterClock);
SV(_ppuBusAddress); SV(_masterClock); SV(_masterClockFrameStart);

if(s.GetFormat() != SerializeFormat::Map) {
//Hide these entries from the Lua API
Expand Down
6 changes: 3 additions & 3 deletions Core/Shared/RenderedFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct RenderedFrame
uint32_t Height = 240;
double Scale = 1.0;
uint32_t FrameNumber = 0;
uint32_t VideoPhase = 0;
uint32_t VideoPhaseOffset = 0;
vector<ControllerData> InputData;

RenderedFrame()
Expand All @@ -27,14 +27,14 @@ struct RenderedFrame
InputData({})
{}

RenderedFrame(void* buffer, uint32_t width, uint32_t height, double scale, uint32_t frameNumber, vector<ControllerData> inputData, uint32_t videoPhase = 0) :
RenderedFrame(void* buffer, uint32_t width, uint32_t height, double scale, uint32_t frameNumber, vector<ControllerData> inputData, uint32_t videoPhaseOffset = 0) :
FrameBuffer(buffer),
Data(nullptr),
Width(width),
Height(height),
Scale(scale),
FrameNumber(frameNumber),
VideoPhase(videoPhase),
VideoPhaseOffset(videoPhaseOffset),
InputData(inputData)
{}
};
8 changes: 4 additions & 4 deletions Core/Shared/Video/BaseVideoFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,22 @@ bool BaseVideoFilter::IsOddFrame()
return _isOddFrame;
}

uint32_t BaseVideoFilter::GetVideoPhase()
uint32_t BaseVideoFilter::GetVideoPhaseOffset()
{
return _videoPhase;
return _videoPhaseOffset;
}

uint32_t BaseVideoFilter::GetBufferSize()
{
return _bufferSize * sizeof(uint32_t);
}

FrameInfo BaseVideoFilter::SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber, uint32_t videoPhase, void* frameData, bool enableOverscan)
FrameInfo BaseVideoFilter::SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber, uint32_t videoPhaseOffset, void* frameData, bool enableOverscan)
{
auto lock = _frameLock.AcquireSafe();
_overscan = enableOverscan ? _emu->GetSettings()->GetOverscan() : OverscanDimensions{};
_isOddFrame = frameNumber % 2;
_videoPhase = videoPhase;
_videoPhaseOffset = videoPhaseOffset;
_frameData = frameData;
_ppuOutputBuffer = ppuOutputBuffer;
OnBeforeApplyFilter();
Expand Down
6 changes: 3 additions & 3 deletions Core/Shared/Video/BaseVideoFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class BaseVideoFilter
SimpleLock _frameLock;
OverscanDimensions _overscan = {};
bool _isOddFrame = false;
uint32_t _videoPhase = 0;
uint32_t _videoPhaseOffset = 0;

void UpdateBufferSize();

Expand All @@ -33,7 +33,7 @@ class BaseVideoFilter
virtual void ApplyFilter(uint16_t *ppuOutputBuffer) = 0;
virtual void OnBeforeApplyFilter();
bool IsOddFrame();
uint32_t GetVideoPhase();
uint32_t GetVideoPhaseOffset();
uint32_t GetBufferSize();

template<typename T> bool NtscFilterOptionsChanged(T& ntscSetup);
Expand All @@ -44,7 +44,7 @@ class BaseVideoFilter
virtual ~BaseVideoFilter();

uint32_t* GetOutputBuffer();
FrameInfo SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber, uint32_t videoPhase, void* frameData, bool enableOverscan = true);
FrameInfo SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber, uint32_t videoPhaseOffset, void* frameData, bool enableOverscan = true);
void TakeScreenshot(string romName, VideoFilterType filterType);
void TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream = nullptr);

Expand Down
2 changes: 1 addition & 1 deletion Core/Shared/Video/VideoDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ void VideoDecoder::DecodeFrame(bool forRewind)
}

_videoFilter->SetBaseFrameInfo(_baseFrameSize);
FrameInfo frameSize = _videoFilter->SendFrame((uint16_t*)_frame.FrameBuffer, _frame.FrameNumber, _frame.VideoPhase, _frame.Data);
FrameInfo frameSize = _videoFilter->SendFrame((uint16_t*)_frame.FrameBuffer, _frame.FrameNumber, _frame.VideoPhaseOffset, _frame.Data);

uint32_t* outputBuffer = _videoFilter->GetOutputBuffer();

Expand Down

0 comments on commit fa7c32f

Please sign in to comment.