Simplify FrameManager state machine, smarter ystart / vblank detection.

This commit is contained in:
Christian Speckner 2016-12-13 22:27:45 +01:00
parent f0828c597d
commit 12fe183890
2 changed files with 87 additions and 11 deletions

View File

@ -22,8 +22,8 @@
#include "FrameManager.hxx" #include "FrameManager.hxx"
enum Metrics: uInt32 { enum Metrics: uInt32 {
vblankNTSC = 40, vblankNTSC = 37,
vblankPAL = 48, vblankPAL = 45,
kernelNTSC = 192, kernelNTSC = 192,
kernelPAL = 228, kernelPAL = 228,
overscanNTSC = 30, overscanNTSC = 30,
@ -31,10 +31,11 @@ enum Metrics: uInt32 {
vsync = 3, vsync = 3,
visibleOverscan = 20, visibleOverscan = 20,
maxUnderscan = 10, maxUnderscan = 10,
maxFramesWithoutVsync = 50,
tvModeDetectionTolerance = 20, tvModeDetectionTolerance = 20,
modeDetectionInitialFrameskip = 5, initialGarbageFrames = 10,
framesForModeConfirmation = 5 framesForModeConfirmation = 5,
maxVblankViolations = 2,
minStableVblankFrames = 1
}; };
static constexpr uInt32 static constexpr uInt32
@ -70,6 +71,11 @@ void FrameManager::reset()
myTotalFrames = 0; myTotalFrames = 0;
myFramesInMode = 0; myFramesInMode = 0;
myModeConfirmed = false; myModeConfirmed = false;
myVblankMode = VblankMode::floating;
myLastVblankLines = 0;
myVblankViolations = 0;
myStableVblankFrames = 0;
myVblankViolated = false;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -85,8 +91,7 @@ void FrameManager::nextLine()
break; break;
case State::waitForFrameStart: case State::waitForFrameStart:
if (myLineInState >= (myVblank ? myVblankLines : myVblankLines - Metrics::maxUnderscan)) nextLineInVsync();
setState(State::frame);
break; break;
case State::frame: case State::frame:
@ -100,6 +105,54 @@ void FrameManager::nextLine()
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::nextLineInVsync()
{
bool shouldTransition = myLineInState >= (myVblank ? myVblankLines : myVblankLines - Metrics::maxUnderscan);
switch (myVblankMode) {
case VblankMode::floating:
if (shouldTransition) {
if (myTotalFrames > initialGarbageFrames && myLineInState == myLastVblankLines)
myStableVblankFrames++;
else
myStableVblankFrames = 0;
myLastVblankLines = myLineInState;
setState(State::frame);
}
if (myStableVblankFrames >= Metrics::minStableVblankFrames) {
myVblankMode = VblankMode::locked;
myVblankViolations = 0;
}
break;
case VblankMode::locked:
if (myLineInState == myLastVblankLines) {
if (shouldTransition && !myVblankViolated)
myVblankViolations = 0;
else {
if (!myVblankViolated) myVblankViolations++;
myVblankViolated = true;
}
setState(State::frame);
} else if (shouldTransition){
if (!myVblankViolated) myVblankViolations++;
myVblankViolated = true;
}
if (myVblankViolations > Metrics::maxVblankViolations)
myVblankMode = VblankMode::floating;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::setVblank(bool vblank) void FrameManager::setVblank(bool vblank)
{ {
@ -123,9 +176,8 @@ void FrameManager::setVsync(bool vsync)
break; break;
case State::waitForVsyncEnd: case State::waitForVsyncEnd:
if (!myVsync) { if (!myVsync)
setState(State::waitForFrameStart); setState(State::waitForFrameStart);
}
break; break;
case State::frame: case State::frame:
@ -210,7 +262,18 @@ void FrameManager::setState(FrameManager::State state)
myState = state; myState = state;
myLineInState = 0; myLineInState = 0;
if (myState == State::frame && myOnFrameStart) myOnFrameStart(); switch (myState) {
case State::waitForFrameStart:
myVblankViolated = false;
break;
case State::frame:
if (myOnFrameStart) myOnFrameStart();
break;
default:
break;
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -230,7 +293,7 @@ void FrameManager::finalizeFrame(FrameManager::State state)
myTotalFrames++; myTotalFrames++;
if (myTotalFrames <= Metrics::modeDetectionInitialFrameskip) { if (myTotalFrames <= Metrics::initialGarbageFrames) {
return; return;
} }

View File

@ -79,6 +79,11 @@ class FrameManager : public Serializable
frame frame
}; };
enum VblankMode {
locked,
floating
};
private: private:
void setTvMode(TvMode mode); void setTvMode(TvMode mode);
@ -87,6 +92,8 @@ class FrameManager : public Serializable
void finalizeFrame(State state = State::waitForVsyncStart); void finalizeFrame(State state = State::waitForVsyncStart);
void nextLineInVsync();
private: private:
callback myOnFrameStart; callback myOnFrameStart;
@ -110,6 +117,12 @@ class FrameManager : public Serializable
uInt32 myOverscanLines; uInt32 myOverscanLines;
uInt32 myFrameLines; uInt32 myFrameLines;
VblankMode myVblankMode;
uInt32 myLastVblankLines;
uInt8 myVblankViolations;
uInt8 myStableVblankFrames;
bool myVblankViolated;
private: private:
FrameManager(const FrameManager&) = delete; FrameManager(const FrameManager&) = delete;
FrameManager(FrameManager&&) = delete; FrameManager(FrameManager&&) = delete;