Refactor FrameManager to extend AbstractFrameManager.

This commit is contained in:
Christian Speckner 2017-10-07 01:41:47 +02:00
parent d52562975d
commit a400238c19
7 changed files with 197 additions and 278 deletions

View File

@ -48,7 +48,7 @@ const DebuggerState& TIADebug::getState()
myState.coluRegs.push_back(coluBK()); myState.coluRegs.push_back(coluBK());
// Debug Colors // Debug Colors
int mode = myTIA.myFrameManager.layout() == FrameLayout::ntsc ? 0 : 1; int mode = myTIA.frameLayout() == FrameLayout::ntsc ? 0 : 1;
myState.fixedCols.clear(); myState.fixedCols.clear();
myState.fixedCols.push_back(myTIA.myFixedColorPalette[mode][TIA::P0]); myState.fixedCols.push_back(myTIA.myFixedColorPalette[mode][TIA::P0]);
myState.fixedCols.push_back(myTIA.myFixedColorPalette[mode][TIA::P1]); myState.fixedCols.push_back(myTIA.myFixedColorPalette[mode][TIA::P1]);
@ -721,7 +721,7 @@ int TIADebug::scanlines() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int TIADebug::scanlinesLastFrame() const int TIADebug::scanlinesLastFrame() const
{ {
return myTIA.myFrameManager.scanlinesLastFrame(); return myTIA.scanlinesLastFrame();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -801,7 +801,7 @@ string TIADebug::debugColors() const
{ {
ostringstream buf; ostringstream buf;
int mode = myTIA.myFrameManager.layout() == FrameLayout::ntsc ? 0 : 1; int mode = myTIA.frameLayout() == FrameLayout::ntsc ? 0 : 1;
buf << " Red " << colorSwatch(myTIA.myFixedColorPalette[mode][TIA::P0]) buf << " Red " << colorSwatch(myTIA.myFixedColorPalette[mode][TIA::P0])
<< " Player 0\n" << " Player 0\n"
<< " Orange " << colorSwatch(myTIA.myFixedColorPalette[mode][TIA::M0]) << " Orange " << colorSwatch(myTIA.myFixedColorPalette[mode][TIA::M0])

View File

@ -22,6 +22,7 @@
#include "Paddles.hxx" #include "Paddles.hxx"
#include "DelayQueueIteratorImpl.hxx" #include "DelayQueueIteratorImpl.hxx"
#include "TIAConstants.hxx" #include "TIAConstants.hxx"
#include "frame-manager/FrameManager.hxx"
#ifdef DEBUGGER_SUPPORT #ifdef DEBUGGER_SUPPORT
#include "CartDebug.hxx" #include "CartDebug.hxx"
@ -77,7 +78,9 @@ TIA::TIA(Console& console, Sound& sound, Settings& settings)
mySpriteEnabledBits(0xFF), mySpriteEnabledBits(0xFF),
myCollisionsEnabledBits(0xFF) myCollisionsEnabledBits(0xFF)
{ {
myFrameManager.setHandlers( myFrameManager = new FrameManager();
myFrameManager->setHandlers(
[this] () { [this] () {
onFrameStart(); onFrameStart();
}, },
@ -99,12 +102,17 @@ TIA::TIA(Console& console, Sound& sound, Settings& settings)
myMissile1.setTIA(this); myMissile1.setTIA(this);
myBall.setTIA(this); myBall.setTIA(this);
myFrameManager.enableJitter(mySettings.getBool("tv.jitter")); myFrameManager->enableJitter(mySettings.getBool("tv.jitter"));
myFrameManager.setJitterFactor(mySettings.getInt("tv.jitter_recovery")); myFrameManager->setJitterFactor(mySettings.getInt("tv.jitter_recovery"));
reset(); reset();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TIA::~TIA() {
delete myFrameManager;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::reset() void TIA::reset()
{ {
@ -144,7 +152,7 @@ void TIA::reset()
mySound.reset(); mySound.reset();
myDelayQueue.reset(); myDelayQueue.reset();
myFrameManager.reset(); myFrameManager->reset();
myCyclesAtFrameStart = 0; myCyclesAtFrameStart = 0;
@ -206,7 +214,7 @@ bool TIA::save(Serializer& out) const
if(!mySound.save(out)) return false; if(!mySound.save(out)) return false;
if(!myDelayQueue.save(out)) return false; if(!myDelayQueue.save(out)) return false;
if(!myFrameManager.save(out)) return false; if(!myFrameManager->save(out)) return false;
if(!myBackground.save(out)) return false; if(!myBackground.save(out)) return false;
if(!myPlayfield.save(out)) return false; if(!myPlayfield.save(out)) return false;
@ -277,7 +285,7 @@ bool TIA::load(Serializer& in)
if(!mySound.load(in)) return false; if(!mySound.load(in)) return false;
if(!myDelayQueue.load(in)) return false; if(!myDelayQueue.load(in)) return false;
if(!myFrameManager.load(in)) return false; if(!myFrameManager->load(in)) return false;
if(!myBackground.load(in)) return false; if(!myBackground.load(in)) return false;
if(!myPlayfield.load(in)) return false; if(!myPlayfield.load(in)) return false;
@ -482,7 +490,7 @@ bool TIA::poke(uInt16 address, uInt8 value)
break; break;
case VSYNC: case VSYNC:
myFrameManager.setVsync(value & 0x02); myFrameManager->setVsync(value & 0x02);
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
@ -796,7 +804,7 @@ bool TIA::enableColorLoss(bool enabled)
if(enabled) if(enabled)
{ {
myColorLossEnabled = true; myColorLossEnabled = true;
myColorLossActive = myFrameManager.scanlinesLastFrame() & 0x1; myColorLossActive = myFrameManager->scanlinesLastFrame() & 0x1;
} }
else else
{ {
@ -820,7 +828,7 @@ bool TIA::electronBeamPos(uInt32& x, uInt32& y) const
uInt8 clocks = clocksThisLine(); uInt8 clocks = clocksThisLine();
x = (clocks < 68) ? 0 : clocks - 68; x = (clocks < 68) ? 0 : clocks - 68;
y = myFrameManager.getY(); y = myFrameManager->getY();
return isRendering(); return isRendering();
} }
@ -906,7 +914,7 @@ bool TIA::toggleCollisions()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIA::enableFixedColors(bool enable) bool TIA::enableFixedColors(bool enable)
{ {
int layout = myFrameManager.layout() == FrameLayout::pal ? 1 : 0; int layout = myFrameManager->layout() == FrameLayout::pal ? 1 : 0;
myMissile0.setDebugColor(myFixedColorPalette[layout][FixedObject::M0]); myMissile0.setDebugColor(myFixedColorPalette[layout][FixedObject::M0]);
myMissile1.setDebugColor(myFixedColorPalette[layout][FixedObject::M1]); myMissile1.setDebugColor(myFixedColorPalette[layout][FixedObject::M1]);
myPlayer0.setDebugColor(myFixedColorPalette[layout][FixedObject::P0]); myPlayer0.setDebugColor(myFixedColorPalette[layout][FixedObject::P0]);
@ -991,22 +999,22 @@ bool TIA::toggleJitter(uInt8 mode)
{ {
switch (mode) { switch (mode) {
case 0: case 0:
myFrameManager.enableJitter(false); myFrameManager->enableJitter(false);
break; break;
case 1: case 1:
myFrameManager.enableJitter(true); myFrameManager->enableJitter(true);
break; break;
case 2: case 2:
myFrameManager.enableJitter(!myFrameManager.jitterEnabled()); myFrameManager->enableJitter(!myFrameManager->jitterEnabled());
break; break;
default: default:
throw runtime_error("invalid argument for toggleJitter"); throw runtime_error("invalid argument for toggleJitter");
} }
return myFrameManager.jitterEnabled(); return myFrameManager->jitterEnabled();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1081,9 +1089,9 @@ void TIA::onFrameStart()
{ {
// Only activate it when necessary, since changing colours in // Only activate it when necessary, since changing colours in
// the graphical object forces the TIA cached line to be flushed // the graphical object forces the TIA cached line to be flushed
if (myFrameManager.scanlineCountTransitioned()) if (myFrameManager->scanlineCountTransitioned())
{ {
myColorLossActive = myFrameManager.scanlinesLastFrame() & 0x1; myColorLossActive = myFrameManager->scanlinesLastFrame() & 0x1;
myMissile0.applyColorLoss(); myMissile0.applyColorLoss();
myMissile1.applyColorLoss(); myMissile1.applyColorLoss();
@ -1112,13 +1120,13 @@ void TIA::onFrameComplete()
memset(myFramebuffer, 0, myXAtRenderingStart); memset(myFramebuffer, 0, myXAtRenderingStart);
// Blank out any extra lines not drawn this frame // Blank out any extra lines not drawn this frame
const uInt32 missingScanlines = myFrameManager.missingScanlines(); const Int32 missingScanlines = myFrameManager->missingScanlines();
if (missingScanlines > 0) if (missingScanlines > 0)
memset(myFramebuffer + 160 * myFrameManager.getY(), 0, missingScanlines * 160); memset(myFramebuffer + 160 * myFrameManager->getY(), 0, missingScanlines * 160);
// Recalculate framerate, attempting to auto-correct for scanline 'jumps' // Recalculate framerate, attempting to auto-correct for scanline 'jumps'
if(myAutoFrameEnabled) if(myAutoFrameEnabled)
myConsole.setFramerate(myFrameManager.frameRate()); myConsole.setFramerate(myFrameManager->frameRate());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1148,7 +1156,7 @@ void TIA::cycle(uInt32 colorClocks)
else else
tickHframe(); tickHframe();
if (myCollisionUpdateRequired && !myFrameManager.vblank()) updateCollision(); if (myCollisionUpdateRequired && !myFrameManager->vblank()) updateCollision();
} }
if (++myHctr >= 228) if (++myHctr >= 228)
@ -1202,7 +1210,7 @@ void TIA::tickHblank()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::tickHframe() void TIA::tickHframe()
{ {
const uInt32 y = myFrameManager.getY(); const uInt32 y = myFrameManager->getY();
const uInt32 x = myHctr - 68 - myHctrDelta; const uInt32 x = myHctr - 68 - myHctrDelta;
myCollisionUpdateRequired = true; myCollisionUpdateRequired = true;
@ -1214,7 +1222,7 @@ void TIA::tickHframe()
myPlayer1.tick(); myPlayer1.tick();
myBall.tick(); myBall.tick();
if (myFrameManager.isRendering()) if (myFrameManager->isRendering())
renderPixel(x, y); renderPixel(x, y);
} }
@ -1224,8 +1232,8 @@ void TIA::applyRsync()
const uInt32 x = myHctr > 68 ? myHctr - 68 : 0; const uInt32 x = myHctr > 68 ? myHctr - 68 : 0;
myHctrDelta = 225 - myHctr; myHctrDelta = 225 - myHctr;
if (myFrameManager.isRendering()) if (myFrameManager->isRendering())
memset(myFramebuffer + myFrameManager.getY() * 160 + x, 0, 160 - x); memset(myFramebuffer + myFrameManager->getY() * 160 + x, 0, 160 - x);
myHctr = 225; myHctr = 225;
} }
@ -1244,9 +1252,9 @@ void TIA::nextLine()
myHstate = HState::blank; myHstate = HState::blank;
myHctrDelta = 0; myHctrDelta = 0;
myFrameManager.nextLine(); myFrameManager->nextLine();
if (myFrameManager.isRendering() && myFrameManager.getY() == 0) flushLineCache(); if (myFrameManager->isRendering() && myFrameManager->getY() == 0) flushLineCache();
mySystem->m6502().clearHaltRequest(); mySystem->m6502().clearHaltRequest();
} }
@ -1254,9 +1262,9 @@ void TIA::nextLine()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::cloneLastLine() void TIA::cloneLastLine()
{ {
const auto y = myFrameManager.getY(); const auto y = myFrameManager->getY();
if (!myFrameManager.isRendering() || y == 0) return; if (!myFrameManager->isRendering() || y == 0) return;
uInt8* buffer = myFramebuffer; uInt8* buffer = myFramebuffer;
@ -1283,7 +1291,7 @@ void TIA::renderPixel(uInt32 x, uInt32 y)
uInt8 color = 0; uInt8 color = 0;
if (!myFrameManager.vblank()) if (!myFrameManager->vblank())
{ {
switch (myPriority) switch (myPriority)
{ {
@ -1356,8 +1364,8 @@ void TIA::flushLineCache()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::clearHmoveComb() void TIA::clearHmoveComb()
{ {
if (myFrameManager.isRendering() && myHstate == HState::blank) if (myFrameManager->isRendering() && myHstate == HState::blank)
memset(myFramebuffer + myFrameManager.getY() * 160, myColorHBlank, 8); memset(myFramebuffer + myFrameManager->getY() * 160, myColorHBlank, 8);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1370,7 +1378,7 @@ void TIA::delayedWrite(uInt8 address, uInt8 value)
{ {
case VBLANK: case VBLANK:
flushLineCache(); flushLineCache();
myFrameManager.setVblank(value & 0x02); myFrameManager->setVblank(value & 0x02);
break; break;
case HMOVE: case HMOVE:

View File

@ -28,7 +28,7 @@
#include "TIAConstants.hxx" #include "TIAConstants.hxx"
#include "DelayQueue.hxx" #include "DelayQueue.hxx"
#include "DelayQueueIterator.hxx" #include "DelayQueueIterator.hxx"
#include "frame-manager/FrameManager.hxx" #include "frame-manager/AbstractFrameManager.hxx"
#include "FrameLayout.hxx" #include "FrameLayout.hxx"
#include "Background.hxx" #include "Background.hxx"
#include "Playfield.hxx" #include "Playfield.hxx"
@ -101,7 +101,7 @@ class TIA : public Device
@param settings The settings object for this TIA device @param settings The settings object for this TIA device
*/ */
TIA(Console& console, Sound& sound, Settings& settings); TIA(Console& console, Sound& sound, Settings& settings);
virtual ~TIA() = default; virtual ~TIA();
public: public:
/** /**
@ -193,21 +193,21 @@ class TIA : public Device
Answers dimensional info about the framebuffer. Answers dimensional info about the framebuffer.
*/ */
uInt32 width() const { return 160; } uInt32 width() const { return 160; }
uInt32 height() const { return myFrameManager.height(); } uInt32 height() const { return myFrameManager->height(); }
uInt32 ystart() const { return myFrameManager.ystart(); } uInt32 ystart() const { return myFrameManager->ystart(); }
bool ystartIsAuto(uInt32 line) const { return myFrameManager.ystartIsAuto(line); } bool ystartIsAuto(uInt32 line) const { return myFrameManager->ystartIsAuto(line); }
/** /**
Changes the current Height/YStart properties. Changes the current Height/YStart properties.
Note that calls to these method(s) must be eventually followed by Note that calls to these method(s) must be eventually followed by
::frameReset() for the changes to take effect. ::frameReset() for the changes to take effect.
*/ */
void setHeight(uInt32 height) { myFrameManager.setFixedHeight(height); } void setHeight(uInt32 height) { myFrameManager->setFixedHeight(height); }
void setYStart(uInt32 ystart) { myFrameManager.setYstart(ystart); } void setYStart(uInt32 ystart) { myFrameManager->setYstart(ystart); }
void autodetectLayout(bool toggle) { myFrameManager.autodetectLayout(toggle); } void autodetectLayout(bool toggle) { myFrameManager->autodetectLayout(toggle); }
void setLayout(FrameLayout layout) { myFrameManager.setLayout(layout); } void setLayout(FrameLayout layout) { myFrameManager->setLayout(layout); }
FrameLayout frameLayout() const { return myFrameManager.layout(); } FrameLayout frameLayout() const { return myFrameManager->layout(); }
/** /**
Answers the timing of the console currently in use. Answers the timing of the console currently in use.
@ -250,7 +250,7 @@ class TIA : public Device
@return The total number of scanlines generated @return The total number of scanlines generated
*/ */
uInt32 scanlines() const { return myFrameManager.scanlines(); } uInt32 scanlines() const { return myFrameManager->scanlines(); }
/** /**
Answers the total number of scanlines the TIA generated in the Answers the total number of scanlines the TIA generated in the
@ -258,7 +258,7 @@ class TIA : public Device
@return The total number of scanlines generated in the last frame. @return The total number of scanlines generated in the last frame.
*/ */
uInt32 scanlinesLastFrame() const { return myFrameManager.scanlinesLastFrame(); } uInt32 scanlinesLastFrame() const { return myFrameManager->scanlinesLastFrame(); }
/** /**
Answers the total system cycles from the start of the emulation. Answers the total system cycles from the start of the emulation.
@ -268,7 +268,7 @@ class TIA : public Device
/** /**
Answers the frame count from the start of the emulation. Answers the frame count from the start of the emulation.
*/ */
uInt32 frameCount() const { return myFrameManager.frameCount(); } uInt32 frameCount() const { return myFrameManager->frameCount(); }
/** /**
Answers the system cycles from the start of the current frame. Answers the system cycles from the start of the current frame.
@ -283,7 +283,7 @@ class TIA : public Device
@return If the frame is in rendering mode @return If the frame is in rendering mode
*/ */
bool isRendering() const { return myFrameManager.isRendering(); } bool isRendering() const { return myFrameManager->isRendering(); }
/** /**
Answers the current position of the virtual 'electron beam' used Answers the current position of the virtual 'electron beam' used
@ -360,7 +360,7 @@ class TIA : public Device
@return Whether the mode was enabled or disabled @return Whether the mode was enabled or disabled
*/ */
bool toggleJitter(uInt8 mode = 2); bool toggleJitter(uInt8 mode = 2);
void setJitterRecoveryFactor(Int32 factor) { myFrameManager.setJitterFactor(factor); } void setJitterRecoveryFactor(Int32 factor) { myFrameManager->setJitterFactor(factor); }
/** /**
This method should be called to update the TIA with a new scanline. This method should be called to update the TIA with a new scanline.
@ -593,7 +593,7 @@ class TIA : public Device
* The frame manager is responsible for detecting frame boundaries and the visible * The frame manager is responsible for detecting frame boundaries and the visible
* region of each frame. * region of each frame.
*/ */
FrameManager myFrameManager; AbstractFrameManager *myFrameManager;
/** /**
* The various TIA objects. * The various TIA objects.

View File

@ -18,8 +18,13 @@
#include "AbstractFrameManager.hxx" #include "AbstractFrameManager.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AbstractFrameManager::AbstractFrameManager() AbstractFrameManager::AbstractFrameManager() :
myLayout(FrameLayout::pal),
myOnFrameStart(0),
myOnFrameComplete(0),
myOnRenderingStart(0)
{ {
layout(FrameLayout::ntsc);
reset(); reset();
} }
@ -33,11 +38,8 @@ void AbstractFrameManager::reset()
myCurrentFrameFinalLines = 0; myCurrentFrameFinalLines = 0;
myPreviousFrameFinalLines = 0; myPreviousFrameFinalLines = 0;
myTotalFrames = 0; myTotalFrames = 0;
myLayout = FrameLayout::ntsc;
myFrameRate = 0; myFrameRate = 0;
myOnFrameComplete = 0; myFrameRate = 60.0;
myOnFrameStart = 0;
myOnRenderingStart = 0;
onReset(); onReset();
} }
@ -96,6 +98,9 @@ void AbstractFrameManager::notifyFrameComplete()
myTotalFrames++; myTotalFrames++;
if (myOnFrameComplete) myOnFrameComplete(); if (myOnFrameComplete) myOnFrameComplete();
myFrameRate = (layout() == FrameLayout::pal ? 15600.0 : 15720.0) /
myCurrentFrameFinalLines;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -104,6 +109,16 @@ void AbstractFrameManager::notifyRenderingStart()
if (myOnRenderingStart) myOnRenderingStart(); if (myOnRenderingStart) myOnRenderingStart();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AbstractFrameManager::layout(FrameLayout layout)
{
if (layout == myLayout) return;
myLayout = layout;
onLayoutChange();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool AbstractFrameManager::save(Serializer& out) const bool AbstractFrameManager::save(Serializer& out) const
{ {

View File

@ -67,6 +67,10 @@ class AbstractFrameManager : public Serializable
float frameRate() const { return myFrameRate; } float frameRate() const { return myFrameRate; }
bool save(Serializer& out) const override;
bool load(Serializer& in) override;
public: public:
virtual void setJitterFactor(uInt8 factor) {} virtual void setJitterFactor(uInt8 factor) {}
@ -75,10 +79,6 @@ class AbstractFrameManager : public Serializable
virtual void enableJitter(bool enabled) {}; virtual void enableJitter(bool enabled) {};
bool save(Serializer& out) const override;
bool load(Serializer& in) override;
public: public:
virtual uInt32 height() const = 0; virtual uInt32 height() const = 0;
@ -89,11 +89,11 @@ class AbstractFrameManager : public Serializable
virtual uInt32 scanlines() const = 0; virtual uInt32 scanlines() const = 0;
virtual uInt32 missingScanlines() const = 0; virtual Int32 missingScanlines() const = 0;
virtual void setYstart(uInt32 ystart) = 0; virtual void setYstart(uInt32 ystart) = 0;
virtual uInt32 ystart() = 0; virtual uInt32 ystart() const = 0;
// TODO: this looks pretty weird --- does this actually work? // TODO: this looks pretty weird --- does this actually work?
virtual bool ystartIsAuto(uInt32 line) const = 0; virtual bool ystartIsAuto(uInt32 line) const = 0;
@ -101,6 +101,7 @@ class AbstractFrameManager : public Serializable
// TODO: this has to go // TODO: this has to go
virtual void autodetectLayout(bool toggle) = 0; virtual void autodetectLayout(bool toggle) = 0;
// TODO: this collides with layout(...). Refactor after all is done.
virtual void setLayout(FrameLayout mode) = 0; virtual void setLayout(FrameLayout mode) = 0;
protected: protected:
@ -113,6 +114,8 @@ class AbstractFrameManager : public Serializable
virtual void onReset() {} virtual void onReset() {}
virtual void onLayoutChange() {}
virtual bool onSave(Serializer& out) const { throw runtime_error("cannot be serialized"); } virtual bool onSave(Serializer& out) const { throw runtime_error("cannot be serialized"); }
virtual bool onLoad(Serializer& in) { throw runtime_error("cannot be serialized"); } virtual bool onLoad(Serializer& in) { throw runtime_error("cannot be serialized"); }
@ -125,6 +128,8 @@ class AbstractFrameManager : public Serializable
void notifyRenderingStart(); void notifyRenderingStart();
void layout(FrameLayout layout);
protected: protected:
bool myIsRendering; bool myIsRendering;
@ -141,12 +146,12 @@ class AbstractFrameManager : public Serializable
uInt32 myTotalFrames; uInt32 myTotalFrames;
FrameLayout myLayout;
float myFrameRate; float myFrameRate;
private: private:
FrameLayout myLayout;
callback myOnFrameStart; callback myOnFrameStart;
callback myOnFrameComplete; callback myOnFrameComplete;
callback myOnRenderingStart; callback myOnRenderingStart;

View File

@ -50,39 +50,19 @@ inline static uInt32 vsyncLimit(bool autodetect) {
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameManager::FrameManager() FrameManager::FrameManager() :
: myLayout(FrameLayout::pal), myHeight(0)
myAutodetectLayout(true),
myHeight(0),
myFixedHeight(0),
myJitterEnabled(false)
{ {
updateLayout(FrameLayout::ntsc); onLayoutChange();
reset();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::setHandlers( void FrameManager::onReset()
FrameManager::callback frameStartCallback,
FrameManager::callback frameCompleteCallback,
FrameManager::callback renderingStartCallback
)
{
myOnFrameStart = frameStartCallback;
myOnFrameComplete = frameCompleteCallback;
myOnRenderingStart = renderingStartCallback;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::reset()
{ {
myVblankManager.reset(); myVblankManager.reset();
myState = State::waitForVsyncStart; myState = State::waitForVsyncStart;
myCurrentFrameTotalLines = myCurrentFrameFinalLines = 0;
myFrameRate = 60.0;
myLineInState = 0; myLineInState = 0;
myVsync = false;
myTotalFrames = 0; myTotalFrames = 0;
myFramesInMode = 0; myFramesInMode = 0;
myModeConfirmed = false; myModeConfirmed = false;
@ -98,11 +78,9 @@ void FrameManager::reset()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::nextLine() void FrameManager::onNextLine()
{ {
State previousState = myState; State previousState = myState;
myCurrentFrameTotalLines++;
myLineInState++; myLineInState++;
switch (myState) switch (myState)
@ -142,25 +120,22 @@ void FrameManager::nextLine()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FrameManager::missingScanlines() const Int32 FrameManager::missingScanlines() const
{ {
if (myLastY == ystart() + myY) if (myLastY == ystart() + myY)
return 0; return 0;
else else {
return myHeight - myY; return myHeight - myY;
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::setVsync(bool vsync) void FrameManager::onSetVsync()
{ {
if (vsync == myVsync) return;
#ifdef TIA_FRAMEMANAGER_DEBUG_LOG #ifdef TIA_FRAMEMANAGER_DEBUG_LOG
(cout << "vsync " << myVsync << " -> " << vsync << ": state " << int(myState) << " @ " << myLineInState << "\n").flush(); (cout << "vsync " << !myVsync << " -> " << myVsync << ": state " << int(myState) << " @ " << myLineInState << "\n").flush();
#endif #endif
myVsync = vsync;
switch (myState) switch (myState)
{ {
case State::waitForVsyncStart: case State::waitForVsyncStart:
@ -200,6 +175,8 @@ void FrameManager::setState(FrameManager::State state)
myStableFrames >= Metrics::minStableFrames || myStableFrames >= Metrics::minStableFrames ||
myStabilizationFrames >= Metrics::maxStabilizationFrames; myStabilizationFrames >= Metrics::maxStabilizationFrames;
updateIsRendering();
myStabilizationFrames++; myStabilizationFrames++;
if (myVblankManager.isStable()) if (myVblankManager.isStable())
@ -209,7 +186,7 @@ void FrameManager::setState(FrameManager::State state)
} }
if (myFramePending) finalizeFrame(); if (myFramePending) finalizeFrame();
if (myOnFrameStart) myOnFrameStart(); notifyFrameStart();
myVblankManager.start(); myVblankManager.start();
myFramePending = true; myFramePending = true;
@ -218,7 +195,7 @@ void FrameManager::setState(FrameManager::State state)
break; break;
case State::frame: case State::frame:
if (myOnRenderingStart) myOnRenderingStart(); notifyRenderingStart();
myVsyncLines = 0; myVsyncLines = 0;
myY = 0; myY = 0;
break; break;
@ -226,6 +203,8 @@ void FrameManager::setState(FrameManager::State state)
default: default:
break; break;
} }
updateIsRendering();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -246,21 +225,13 @@ void FrameManager::finalizeFrame()
else myStableFrameHeightCountdown = 0; else myStableFrameHeightCountdown = 0;
} }
myPreviousFrameFinalLines = myCurrentFrameFinalLines; notifyFrameComplete();
myCurrentFrameFinalLines = myCurrentFrameTotalLines;
myCurrentFrameTotalLines = 0;
myTotalFrames++;
if (myOnFrameComplete) myOnFrameComplete();
#ifdef TIA_FRAMEMANAGER_DEBUG_LOG #ifdef TIA_FRAMEMANAGER_DEBUG_LOG
(cout << "frame complete @ " << myLineInState << " (" << myCurrentFrameFinalLines << " total)" << "\n").flush(); (cout << "frame complete @ " << myLineInState << " (" << myCurrentFrameFinalLines << " total)" << "\n").flush();
#endif // TIA_FRAMEMANAGER_DEBUG_LOG #endif // TIA_FRAMEMANAGER_DEBUG_LOG
if (myAutodetectLayout) updateAutodetectedLayout(); if (myAutodetectLayout) updateAutodetectedLayout();
myFrameRate = (myLayout == FrameLayout::pal ? 15600.0 : 15720.0) /
myCurrentFrameFinalLines;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -282,26 +253,26 @@ void FrameManager::updateAutodetectedLayout()
return; return;
} }
const FrameLayout oldLayout = myLayout; const FrameLayout oldLayout = layout();
const uInt32 const uInt32
deltaNTSC = abs(Int32(myCurrentFrameFinalLines) - Int32(frameLinesNTSC)), deltaNTSC = abs(Int32(myCurrentFrameFinalLines) - Int32(frameLinesNTSC)),
deltaPAL = abs(Int32(myCurrentFrameFinalLines) - Int32(frameLinesPAL)); deltaPAL = abs(Int32(myCurrentFrameFinalLines) - Int32(frameLinesPAL));
if (std::min(deltaNTSC, deltaPAL) <= Metrics::tvModeDetectionTolerance) if (std::min(deltaNTSC, deltaPAL) <= Metrics::tvModeDetectionTolerance)
updateLayout(deltaNTSC <= deltaPAL ? FrameLayout::ntsc : FrameLayout::pal); layout(deltaNTSC <= deltaPAL ? FrameLayout::ntsc : FrameLayout::pal);
else if (!myModeConfirmed) { else if (!myModeConfirmed) {
if ( if (
(myCurrentFrameFinalLines < frameLinesPAL) && (myCurrentFrameFinalLines < frameLinesPAL) &&
(myCurrentFrameFinalLines > frameLinesNTSC) && (myCurrentFrameFinalLines > frameLinesNTSC) &&
(myCurrentFrameFinalLines % 2) (myCurrentFrameFinalLines % 2)
) )
updateLayout(FrameLayout::ntsc); layout(FrameLayout::ntsc);
else else
updateLayout(deltaNTSC <= deltaPAL ? FrameLayout::ntsc : FrameLayout::pal); layout(deltaNTSC <= deltaPAL ? FrameLayout::ntsc : FrameLayout::pal);
} }
if (oldLayout == myLayout) if (oldLayout == layout())
myFramesInMode++; myFramesInMode++;
else else
myFramesInMode = 0; myFramesInMode = 0;
@ -311,17 +282,13 @@ void FrameManager::updateAutodetectedLayout()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::updateLayout(FrameLayout layout) void FrameManager::onLayoutChange()
{ {
if (layout == myLayout) return;
#ifdef TIA_FRAMEMANAGER_DEBUG_LOG #ifdef TIA_FRAMEMANAGER_DEBUG_LOG
(cout << "TV mode switched to " << int(layout) << "\n").flush(); (cout << "TV mode switched to " << int(layout()) << "\n").flush();
#endif // TIA_FRAMEMANAGER_DEBUG_LOG #endif // TIA_FRAMEMANAGER_DEBUG_LOG
myLayout = layout; switch (layout())
switch (myLayout)
{ {
case FrameLayout::ntsc: case FrameLayout::ntsc:
myVblankLines = Metrics::vblankNTSC; myVblankLines = Metrics::vblankNTSC;
@ -347,18 +314,18 @@ void FrameManager::updateLayout(FrameLayout layout)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::setVblank(bool vblank) void FrameManager::onSetVblank()
{ {
#ifdef TIA_FRAMEMANAGER_DEBUG_LOG #ifdef TIA_FRAMEMANAGER_DEBUG_LOG
(cout << "vblank change " << myVblankManager.vblank() << " -> " << vblank << "@" << myLineInState << "\n").flush(); (cout << "vblank change " << !myVblank << " -> " << myVblank << "@" << myLineInState << "\n").flush();
#endif // TIA_FRAMEMANAGER_DEBUG_LOG #endif // TIA_FRAMEMANAGER_DEBUG_LOG
if (myState == State::waitForFrameStart) { if (myState == State::waitForFrameStart) {
if (myVblankManager.setVblankDuringVblank(vblank, myTotalFrames <= Metrics::initialGarbageFrames)) { if (myVblankManager.setVblankDuringVblank(myVblank, myTotalFrames <= Metrics::initialGarbageFrames)) {
setState(State::frame); setState(State::frame);
} }
} else } else
myVblankManager.setVblank(vblank); myVblankManager.setVblank(myVblank);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -377,106 +344,76 @@ void FrameManager::enableJitter(bool enabled)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameManager::save(Serializer& out) const void FrameManager::updateIsRendering() {
myIsRendering = myState == State::frame && myHasStabilized;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameManager::onSave(Serializer& out) const
{ {
try if (!myVblankManager.save(out)) return false;
{
out.putString(name());
if (!myVblankManager.save(out)) return false; out.putBool(myAutodetectLayout);
out.putInt(uInt32(myState));
out.putInt(myLineInState);
out.putInt(myVsyncLines);
out.putInt(myY);
out.putInt(myLastY);
out.putBool(myFramePending);
out.putInt(uInt32(myLayout)); out.putInt(myFramesInMode);
out.putBool(myAutodetectLayout); out.putBool(myModeConfirmed);
out.putInt(uInt32(myState));
out.putInt(myLineInState);
out.putInt(myCurrentFrameTotalLines);
out.putInt(myCurrentFrameFinalLines);
out.putInt(myPreviousFrameFinalLines);
out.putInt(myVsyncLines);
out.putDouble(myFrameRate);
out.putInt(myY); out.putInt(myLastY);
out.putBool(myFramePending);
out.putInt(myTotalFrames); out.putInt(myStableFrames);
out.putInt(myFramesInMode); out.putInt(myStabilizationFrames);
out.putBool(myModeConfirmed); out.putBool(myHasStabilized);
out.putInt(myStableFrames); out.putInt(myVblankLines);
out.putInt(myStabilizationFrames); out.putInt(myKernelLines);
out.putBool(myHasStabilized); out.putInt(myOverscanLines);
out.putInt(myFrameLines);
out.putInt(myHeight);
out.putInt(myFixedHeight);
out.putBool(myVsync); out.putBool(myJitterEnabled);
out.putInt(myVblankLines); out.putInt(myStableFrameLines);
out.putInt(myKernelLines); out.putInt(myStableFrameHeightCountdown);
out.putInt(myOverscanLines);
out.putInt(myFrameLines);
out.putInt(myHeight);
out.putInt(myFixedHeight);
out.putBool(myJitterEnabled);
out.putInt(myStableFrameLines);
out.putInt(myStableFrameHeightCountdown);
}
catch(...)
{
cerr << "ERROR: TIA_FrameManager::save" << endl;
return false;
}
return true; return true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameManager::load(Serializer& in) bool FrameManager::onLoad(Serializer& in)
{ {
try if (!myVblankManager.load(in)) return false;
{
if(in.getString() != name())
return false;
if (!myVblankManager.load(in)) return false; myAutodetectLayout = in.getBool();
myState = State(in.getInt());
myLineInState = in.getInt();
myVsyncLines = in.getInt();
myY = in.getInt();
myLastY = in.getInt();
myFramePending = in.getBool();
myLayout = FrameLayout(in.getInt()); myFramesInMode = in.getInt();
myAutodetectLayout = in.getBool(); myModeConfirmed = in.getBool();
myState = State(in.getInt());
myLineInState = in.getInt();
myCurrentFrameTotalLines = in.getInt();
myCurrentFrameFinalLines = in.getInt();
myPreviousFrameFinalLines = in.getInt();
myVsyncLines = in.getInt();
myFrameRate = float(in.getDouble());
myY = in.getInt(); myLastY = in.getInt();
myFramePending = in.getBool();
myTotalFrames = in.getInt(); myStableFrames = in.getInt();
myFramesInMode = in.getInt(); myStabilizationFrames = in.getInt();
myModeConfirmed = in.getBool(); myHasStabilized = in.getBool();
myStableFrames = in.getInt(); myVblankLines = in.getInt();
myStabilizationFrames = in.getInt(); myKernelLines = in.getInt();
myHasStabilized = in.getBool(); myOverscanLines = in.getInt();
myFrameLines = in.getInt();
myHeight = in.getInt();
myFixedHeight = in.getInt();
myVsync = in.getBool(); myJitterEnabled = in.getBool();
myVblankLines = in.getInt(); myStableFrameLines = in.getInt();
myKernelLines = in.getInt(); myStableFrameHeightCountdown = in.getInt();
myOverscanLines = in.getInt();
myFrameLines = in.getInt();
myHeight = in.getInt();
myFixedHeight = in.getInt();
myJitterEnabled = in.getBool();
myStableFrameLines = in.getInt();
myStableFrameHeightCountdown = in.getInt();
}
catch(...)
{
cerr << "ERROR: TIA_FrameManager::load" << endl;
return false;
}
return true; return true;
} }

View File

@ -18,88 +18,60 @@
#ifndef TIA_FRAME_MANAGER #ifndef TIA_FRAME_MANAGER
#define TIA_FRAME_MANAGER #define TIA_FRAME_MANAGER
#include <functional> #include "AbstractFrameManager.hxx"
#include "VblankManager.hxx" #include "VblankManager.hxx"
#include "Serializable.hxx"
#include "FrameLayout.hxx"
#include "TIAConstants.hxx" #include "TIAConstants.hxx"
#include "bspf.hxx" #include "bspf.hxx"
class FrameManager : public Serializable class FrameManager: public AbstractFrameManager {
{
public:
using callback = std::function<void()>;
public: public:
FrameManager(); FrameManager();
public: public:
void setHandlers( void setJitterFactor(uInt8 factor) override { myVblankManager.setJitterFactor(factor); }
callback frameStartCallback,
callback frameCompletionCallback,
callback renderingStartCallback
);
void reset(); bool jitterEnabled() const override { return myJitterEnabled; }
void nextLine(); void enableJitter(bool enabled) override;
void setVblank(bool vblank); uInt32 height() const override { return myHeight; };
void setVsync(bool vsync); void setFixedHeight(uInt32 height) override;
bool isRendering() const { return myState == State::frame && myHasStabilized; } uInt32 getY() const override { return myY; }
FrameLayout layout() const { return myLayout; } uInt32 scanlines() const override { return myCurrentFrameTotalLines; }
bool vblank() const { return myVblankManager.vblank(); } Int32 missingScanlines() const override;
bool vsync() const { return myVsync; } void setYstart(uInt32 ystart) override { myVblankManager.setYstart(ystart); }
uInt32 height() const { return myHeight; } uInt32 ystart() const override { return myVblankManager.ystart(); }
void setFixedHeight(uInt32 height); bool ystartIsAuto(uInt32 line) const override { return myVblankManager.ystartIsAuto(line); };
uInt32 getY() const { return myY; } void autodetectLayout(bool toggle) override { myAutodetectLayout = toggle; }
uInt32 scanlines() const { return myCurrentFrameTotalLines; } void setLayout(FrameLayout mode) override { if (!myAutodetectLayout) layout(mode); }
uInt32 scanlinesLastFrame() const { return myCurrentFrameFinalLines; } void onSetVblank() override;
uInt32 missingScanlines() const; void onSetVsync() override;
bool scanlineCountTransitioned() const { void onNextLine() override;
return (myPreviousFrameFinalLines & 0x1) != (myCurrentFrameFinalLines & 0x1);
}
uInt32 frameCount() const { return myTotalFrames; } void onReset() override;
float frameRate() const { return myFrameRate; } void onLayoutChange() override;
void setYstart(uInt32 ystart) { myVblankManager.setYstart(ystart); } bool onSave(Serializer& out) const override;
uInt32 ystart() const { return myVblankManager.ystart(); } bool onLoad(Serializer& in) override;
bool ystartIsAuto(uInt32 line) const { return myVblankManager.ystartIsAuto(line); }
void autodetectLayout(bool toggle) { myAutodetectLayout = toggle; }
void setLayout(FrameLayout mode) { if (!myAutodetectLayout) updateLayout(mode); }
/**
Serializable methods (see that class for more information).
*/
bool save(Serializer& out) const override;
bool load(Serializer& in) override;
string name() const override { return "TIA_FrameManager"; } string name() const override { return "TIA_FrameManager"; }
void setJitterFactor(uInt8 factor) { myVblankManager.setJitterFactor(factor); }
bool jitterEnabled() const { return myJitterEnabled; }
void enableJitter(bool enabled);
private: private:
enum State { enum State {
@ -109,17 +81,8 @@ class FrameManager : public Serializable
frame frame
}; };
enum VblankMode {
locked,
floating,
final,
fixed
};
private: private:
void updateLayout(FrameLayout mode);
void updateAutodetectedLayout(); void updateAutodetectedLayout();
void setState(State state); void setState(State state);
@ -130,27 +93,19 @@ class FrameManager : public Serializable
void handleJitter(Int32 scanlineDifference); void handleJitter(Int32 scanlineDifference);
private: void updateIsRendering();
callback myOnFrameStart; private:
callback myOnFrameComplete;
callback myOnRenderingStart;
VblankManager myVblankManager; VblankManager myVblankManager;
FrameLayout myLayout;
bool myAutodetectLayout; bool myAutodetectLayout;
State myState; State myState;
uInt32 myLineInState; uInt32 myLineInState;
uInt32 myCurrentFrameTotalLines;
uInt32 myCurrentFrameFinalLines;
uInt32 myPreviousFrameFinalLines;
uInt32 myVsyncLines; uInt32 myVsyncLines;
float myFrameRate;
uInt32 myY, myLastY; uInt32 myY, myLastY;
bool myFramePending; bool myFramePending;
uInt32 myTotalFrames;
uInt32 myFramesInMode; uInt32 myFramesInMode;
bool myModeConfirmed; bool myModeConfirmed;
@ -158,8 +113,6 @@ class FrameManager : public Serializable
uInt32 myStabilizationFrames; uInt32 myStabilizationFrames;
bool myHasStabilized; bool myHasStabilized;
bool myVsync;
uInt32 myVblankLines; uInt32 myVblankLines;
uInt32 myKernelLines; uInt32 myKernelLines;
uInt32 myOverscanLines; uInt32 myOverscanLines;
@ -173,6 +126,7 @@ class FrameManager : public Serializable
uInt8 myStableFrameHeightCountdown; uInt8 myStableFrameHeightCountdown;
private: private:
FrameManager(const FrameManager&) = delete; FrameManager(const FrameManager&) = delete;
FrameManager(FrameManager&&) = delete; FrameManager(FrameManager&&) = delete;
FrameManager& operator=(const FrameManager&) = delete; FrameManager& operator=(const FrameManager&) = delete;