Added netplay client frame throttling functionality to keep it in step with server.
This commit is contained in:
parent
798c5a1d9c
commit
72d1a8edf2
|
@ -71,13 +71,14 @@ struct NetPlayFrameDataHist_t
|
||||||
int getLast( NetPlayFrameData& out )
|
int getLast( NetPlayFrameData& out )
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
if (bufHead == 0)
|
const int head = bufHead;
|
||||||
|
if (head == 0)
|
||||||
{
|
{
|
||||||
i = numFrames - 1;
|
i = numFrames - 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
i = bufHead - 1;
|
i = head - 1;
|
||||||
}
|
}
|
||||||
out = data[i];
|
out = data[i];
|
||||||
|
|
||||||
|
@ -87,17 +88,18 @@ struct NetPlayFrameDataHist_t
|
||||||
int find( uint32_t frame, NetPlayFrameData& out )
|
int find( uint32_t frame, NetPlayFrameData& out )
|
||||||
{
|
{
|
||||||
int i, retval = -1;
|
int i, retval = -1;
|
||||||
|
const int head = bufHead;
|
||||||
|
|
||||||
if (bufHead == 0)
|
if (head == 0)
|
||||||
{
|
{
|
||||||
i = numFrames - 1;
|
i = numFrames - 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
i = bufHead - 1;
|
i = head - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (i != bufHead)
|
while (i != head)
|
||||||
{
|
{
|
||||||
if (data[i].frameNum == frame)
|
if (data[i].frameNum == frame)
|
||||||
{
|
{
|
||||||
|
@ -173,6 +175,10 @@ NetPlayServer::NetPlayServer(QObject *parent)
|
||||||
|
|
||||||
connect(consoleWindow, SIGNAL(romLoaded(void)), this, SLOT(onRomLoad(void)));
|
connect(consoleWindow, SIGNAL(romLoaded(void)), this, SLOT(onRomLoad(void)));
|
||||||
connect(consoleWindow, SIGNAL(nesResetOccurred(void)), this, SLOT(onNesReset(void)));
|
connect(consoleWindow, SIGNAL(nesResetOccurred(void)), this, SLOT(onNesReset(void)));
|
||||||
|
|
||||||
|
FCEU_WRAPPER_LOCK();
|
||||||
|
inputFrameCount = static_cast<uint32_t>(currFrameCounter);
|
||||||
|
FCEU_WRAPPER_UNLOCK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -417,6 +423,10 @@ void NetPlayServer::onRomLoad()
|
||||||
{
|
{
|
||||||
//printf("New ROM Loaded!\n");
|
//printf("New ROM Loaded!\n");
|
||||||
FCEU_WRAPPER_LOCK();
|
FCEU_WRAPPER_LOCK();
|
||||||
|
|
||||||
|
inputClear();
|
||||||
|
inputFrameCount = static_cast<uint32_t>(currFrameCounter);
|
||||||
|
|
||||||
// New ROM has been loaded by server, signal clients to load and sync
|
// New ROM has been loaded by server, signal clients to load and sync
|
||||||
for (auto& client : clientList )
|
for (auto& client : clientList )
|
||||||
{
|
{
|
||||||
|
@ -430,6 +440,10 @@ void NetPlayServer::onNesReset()
|
||||||
{
|
{
|
||||||
//printf("New ROM Loaded!\n");
|
//printf("New ROM Loaded!\n");
|
||||||
FCEU_WRAPPER_LOCK();
|
FCEU_WRAPPER_LOCK();
|
||||||
|
|
||||||
|
inputClear();
|
||||||
|
inputFrameCount = static_cast<uint32_t>(currFrameCounter);
|
||||||
|
|
||||||
// NES Reset has occurred on server, signal clients sync
|
// NES Reset has occurred on server, signal clients sync
|
||||||
for (auto& client : clientList )
|
for (auto& client : clientList )
|
||||||
{
|
{
|
||||||
|
@ -663,7 +677,7 @@ void NetPlayServer::update(void)
|
||||||
const uint32_t maxLead = maxLeadFrames;
|
const uint32_t maxLead = maxLeadFrames;
|
||||||
const uint32_t currFrame = static_cast<uint32_t>(currFrameCounter);
|
const uint32_t currFrame = static_cast<uint32_t>(currFrameCounter);
|
||||||
const uint32_t leadFrame = currFrame + maxLead;
|
const uint32_t leadFrame = currFrame + maxLead;
|
||||||
const uint32_t lastFrame = inputFrameBack();
|
//const uint32_t lastFrame = inputFrameBack();
|
||||||
uint32_t lagFrame = 0;
|
uint32_t lagFrame = 0;
|
||||||
uint8_t localGP[4] = { 0 };
|
uint8_t localGP[4] = { 0 };
|
||||||
uint8_t gpData[4] = { 0 };
|
uint8_t gpData[4] = { 0 };
|
||||||
|
@ -735,7 +749,7 @@ void NetPlayServer::update(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hostRdyFrame = ( (currFrame > lastFrame) || (lastFrame == 0) );
|
hostRdyFrame = (currFrame >= inputFrameCount);
|
||||||
|
|
||||||
shouldRunFrame = (clientMinFrame != 0xFFFFFFFF) &&
|
shouldRunFrame = (clientMinFrame != 0xFFFFFFFF) &&
|
||||||
(clientMinFrame >= lagFrame ) &&
|
(clientMinFrame >= lagFrame ) &&
|
||||||
|
@ -752,6 +766,7 @@ void NetPlayServer::update(void)
|
||||||
clientWaitCounter = 0;
|
clientWaitCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//printf("Host Frame: Run:%u Input:%u Last:%u\n", currFrame, inputFrameCount, lastFrame);
|
||||||
//printf("Client Frame: Min:%u Max:%u\n", clientMinFrame, clientMaxFrame);
|
//printf("Client Frame: Min:%u Max:%u\n", clientMinFrame, clientMaxFrame);
|
||||||
|
|
||||||
if (shouldRunFrame)
|
if (shouldRunFrame)
|
||||||
|
@ -760,7 +775,7 @@ void NetPlayServer::update(void)
|
||||||
NetPlayFrameInput inputFrame;
|
NetPlayFrameInput inputFrame;
|
||||||
netPlayRunFrameReq runFrameReq;
|
netPlayRunFrameReq runFrameReq;
|
||||||
|
|
||||||
inputFrame.frameCounter = static_cast<uint32_t>(currFrameCounter) + 1;
|
inputFrame.frameCounter = ++inputFrameCount;
|
||||||
inputFrame.ctrl[0] = gpData[0];
|
inputFrame.ctrl[0] = gpData[0];
|
||||||
inputFrame.ctrl[1] = gpData[1];
|
inputFrame.ctrl[1] = gpData[1];
|
||||||
inputFrame.ctrl[2] = gpData[2];
|
inputFrame.ctrl[2] = gpData[2];
|
||||||
|
@ -772,6 +787,13 @@ void NetPlayServer::update(void)
|
||||||
runFrameReq.ctrlState[2] = inputFrame.ctrl[2];
|
runFrameReq.ctrlState[2] = inputFrame.ctrl[2];
|
||||||
runFrameReq.ctrlState[3] = inputFrame.ctrl[3];
|
runFrameReq.ctrlState[3] = inputFrame.ctrl[3];
|
||||||
|
|
||||||
|
uint32_t catchUpThreshold = maxLead;
|
||||||
|
if (catchUpThreshold < 3)
|
||||||
|
{
|
||||||
|
catchUpThreshold = 3;
|
||||||
|
}
|
||||||
|
runFrameReq.catchUpThreshold = catchUpThreshold;
|
||||||
|
|
||||||
pushBackInput( inputFrame );
|
pushBackInput( inputFrame );
|
||||||
|
|
||||||
runFrameReq.toNetworkByteOrder();
|
runFrameReq.toNetworkByteOrder();
|
||||||
|
@ -1109,15 +1131,21 @@ int NetPlayClient::readMessages( void (*msgCallback)( void *userData, void *msgB
|
||||||
{
|
{
|
||||||
bool readReq;
|
bool readReq;
|
||||||
netPlayMsgHdr *hdr;
|
netPlayMsgHdr *hdr;
|
||||||
const int netPlayMsgHdrSize = sizeof(netPlayMsgHdr);
|
constexpr int netPlayMsgHdrSize = sizeof(netPlayMsgHdr);
|
||||||
|
FCEU::timeStampRecord ts;
|
||||||
|
|
||||||
readReq = sock->bytesAvailable() > 0;
|
ts.readNew();
|
||||||
|
int bytesAvailable = sock->bytesAvailable();
|
||||||
|
readReq = bytesAvailable > 0;
|
||||||
|
|
||||||
|
//printf("Read Bytes Available: %lu %i\n", ts.toMilliSeconds(), bytesAvailable);
|
||||||
|
|
||||||
while (readReq)
|
while (readReq)
|
||||||
{
|
{
|
||||||
if (recvMsgBytesLeft > 0)
|
if (recvMsgBytesLeft > 0)
|
||||||
{
|
{
|
||||||
readReq = (sock->bytesAvailable() >= recvMsgBytesLeft);
|
bytesAvailable = sock->bytesAvailable();
|
||||||
|
readReq = (bytesAvailable >= recvMsgBytesLeft);
|
||||||
|
|
||||||
if (readReq)
|
if (readReq)
|
||||||
{
|
{
|
||||||
|
@ -1135,16 +1163,18 @@ int NetPlayClient::readMessages( void (*msgCallback)( void *userData, void *msgB
|
||||||
|
|
||||||
if (recvMsgBytesLeft > 0)
|
if (recvMsgBytesLeft > 0)
|
||||||
{
|
{
|
||||||
readReq = (sock->bytesAvailable() >= recvMsgBytesLeft);
|
bytesAvailable = sock->bytesAvailable();
|
||||||
|
readReq = (bytesAvailable >= recvMsgBytesLeft);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
msgCallback( userData, recvMsgBuf, recvMsgSize );
|
msgCallback( userData, recvMsgBuf, recvMsgSize );
|
||||||
readReq = (sock->bytesAvailable() > 0);
|
bytesAvailable = sock->bytesAvailable();
|
||||||
|
readReq = (bytesAvailable > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (sock->bytesAvailable() >= netPlayMsgHdrSize)
|
else if (bytesAvailable >= netPlayMsgHdrSize)
|
||||||
{
|
{
|
||||||
sock->read( recvMsgBuf, netPlayMsgHdrSize );
|
sock->read( recvMsgBuf, netPlayMsgHdrSize );
|
||||||
|
|
||||||
|
@ -1166,7 +1196,8 @@ int NetPlayClient::readMessages( void (*msgCallback)( void *userData, void *msgB
|
||||||
{
|
{
|
||||||
msgCallback( userData, recvMsgBuf, recvMsgSize );
|
msgCallback( userData, recvMsgBuf, recvMsgSize );
|
||||||
}
|
}
|
||||||
readReq = (sock->bytesAvailable() >= recvMsgSize);
|
bytesAvailable = sock->bytesAvailable();
|
||||||
|
readReq = (bytesAvailable >= recvMsgSize);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1269,10 +1300,20 @@ void NetPlayClient::clientProcessMessage( void *msgBuf, size_t msgSize )
|
||||||
inputFrame.ctrl[2] = msg->ctrlState[2];
|
inputFrame.ctrl[2] = msg->ctrlState[2];
|
||||||
inputFrame.ctrl[3] = msg->ctrlState[3];
|
inputFrame.ctrl[3] = msg->ctrlState[3];
|
||||||
|
|
||||||
if (inputFrame.frameCounter > inputFrameBack())
|
catchUpThreshold = msg->catchUpThreshold;
|
||||||
|
|
||||||
|
uint32_t lastInputFrame = inputFrameBack();
|
||||||
|
uint32_t currFrame = static_cast<uint32_t>(currFrameCounter);
|
||||||
|
|
||||||
|
if (inputFrame.frameCounter > lastInputFrame)
|
||||||
{
|
{
|
||||||
pushBackInput( inputFrame );
|
pushBackInput( inputFrame );
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Drop Frame: LastRun:%u LastInput:%u NewInput:%u\n", currFrame, lastInputFrame, inputFrame.frameCounter);
|
||||||
|
}
|
||||||
|
//printf("Run Frame: LastRun:%u LastInput:%u NewInput:%u\n", currFrame, lastInputFrame, inputFrame.frameCounter);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NETPLAY_PING_REQ:
|
case NETPLAY_PING_REQ:
|
||||||
|
@ -1302,6 +1343,8 @@ NetPlayHostDialog::NetPlayHostDialog(QWidget *parent)
|
||||||
QVBoxLayout *mainLayout;
|
QVBoxLayout *mainLayout;
|
||||||
QHBoxLayout *hbox;
|
QHBoxLayout *hbox;
|
||||||
QGridLayout *grid;
|
QGridLayout *grid;
|
||||||
|
QGroupBox *networkGroupBox;
|
||||||
|
QGroupBox *settingsGroupBox;
|
||||||
QPushButton *cancelButton, *startButton;
|
QPushButton *cancelButton, *startButton;
|
||||||
QLabel *lbl;
|
QLabel *lbl;
|
||||||
|
|
||||||
|
@ -1310,8 +1353,18 @@ NetPlayHostDialog::NetPlayHostDialog(QWidget *parent)
|
||||||
setWindowTitle("NetPlay Host Game");
|
setWindowTitle("NetPlay Host Game");
|
||||||
|
|
||||||
mainLayout = new QVBoxLayout();
|
mainLayout = new QVBoxLayout();
|
||||||
|
networkGroupBox = new QGroupBox(tr("Network Setup"));
|
||||||
|
settingsGroupBox = new QGroupBox(tr("Server Settings"));
|
||||||
|
hbox = new QHBoxLayout();
|
||||||
grid = new QGridLayout();
|
grid = new QGridLayout();
|
||||||
|
|
||||||
|
mainLayout->addLayout(hbox);
|
||||||
|
hbox->addWidget(networkGroupBox);
|
||||||
|
hbox->addWidget(settingsGroupBox);
|
||||||
|
|
||||||
|
// Network Settings
|
||||||
|
networkGroupBox->setLayout(grid);
|
||||||
|
|
||||||
lbl = new QLabel( tr("Server Name:") );
|
lbl = new QLabel( tr("Server Name:") );
|
||||||
grid->addWidget( lbl, 0, 0 );
|
grid->addWidget( lbl, 0, 0 );
|
||||||
|
|
||||||
|
@ -1350,7 +1403,22 @@ NetPlayHostDialog::NetPlayHostDialog(QWidget *parent)
|
||||||
passwordEntry = new QLineEdit();
|
passwordEntry = new QLineEdit();
|
||||||
grid->addWidget( passwordEntry, 4, 1 );
|
grid->addWidget( passwordEntry, 4, 1 );
|
||||||
|
|
||||||
mainLayout->addLayout(grid);
|
// Misc Settings
|
||||||
|
grid = new QGridLayout();
|
||||||
|
settingsGroupBox->setLayout(grid);
|
||||||
|
|
||||||
|
lbl = new QLabel( tr("Max Frame Lead:") );
|
||||||
|
frameLeadSpinBox = new QSpinBox();
|
||||||
|
frameLeadSpinBox->setRange(5,60);
|
||||||
|
frameLeadSpinBox->setValue(30);
|
||||||
|
grid->addWidget( lbl, 0, 0, 1, 1 );
|
||||||
|
grid->addWidget( frameLeadSpinBox, 0, 1, 1, 1 );
|
||||||
|
|
||||||
|
allowClientRomReqCBox = new QCheckBox(tr("Allow Client ROM Load Requests"));
|
||||||
|
grid->addWidget( allowClientRomReqCBox, 1, 0, 1, 2 );
|
||||||
|
|
||||||
|
allowClientStateReqCBox = new QCheckBox(tr("Allow Client State Load Requests"));
|
||||||
|
grid->addWidget( allowClientStateReqCBox, 2, 0, 1, 2 );
|
||||||
|
|
||||||
startButton = new QPushButton( tr("Start") );
|
startButton = new QPushButton( tr("Start") );
|
||||||
startButton->setIcon(style()->standardIcon(QStyle::SP_DialogApplyButton));
|
startButton->setIcon(style()->standardIcon(QStyle::SP_DialogApplyButton));
|
||||||
|
@ -1406,6 +1474,9 @@ void NetPlayHostDialog::onStartClicked(void)
|
||||||
server->setRole( playerRoleBox->currentData().toInt() );
|
server->setRole( playerRoleBox->currentData().toInt() );
|
||||||
server->sessionName = sessionNameEntry->text();
|
server->sessionName = sessionNameEntry->text();
|
||||||
server->sessionPasswd = passwordEntry->text();
|
server->sessionPasswd = passwordEntry->text();
|
||||||
|
server->setMaxLeadFrames( frameLeadSpinBox->value() );
|
||||||
|
server->setAllowClientRomLoadRequest( allowClientRomReqCBox->isChecked() );
|
||||||
|
server->setAllowClientStateLoadRequest( allowClientStateReqCBox->isChecked() );
|
||||||
|
|
||||||
bool listenSucceeded = server->listen( QHostAddress::Any, netPort );
|
bool listenSucceeded = server->listen( QHostAddress::Any, netPort );
|
||||||
|
|
||||||
|
@ -1503,6 +1574,7 @@ NetPlayJoinDialog::NetPlayJoinDialog(QWidget *parent)
|
||||||
|
|
||||||
passwordEntry = new QLineEdit();
|
passwordEntry = new QLineEdit();
|
||||||
passwordEntry->setMaxLength(64);
|
passwordEntry->setMaxLength(64);
|
||||||
|
passwordEntry->setEnabled(false);
|
||||||
grid->addWidget( passwordEntry, 4, 1 );
|
grid->addWidget( passwordEntry, 4, 1 );
|
||||||
|
|
||||||
mainLayout->addLayout(grid);
|
mainLayout->addLayout(grid);
|
||||||
|
@ -1944,7 +2016,7 @@ bool NetPlaySkipWait(void)
|
||||||
|
|
||||||
if (client)
|
if (client)
|
||||||
{
|
{
|
||||||
skip = client->inputAvailable() > 1;
|
skip = client->inputAvailableCount() > client->catchUpThreshold;
|
||||||
}
|
}
|
||||||
return skip;
|
return skip;
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,6 +139,8 @@ class NetPlayServer : public QTcpServer
|
||||||
|
|
||||||
uint32_t getMaxLeadFrames(){ return maxLeadFrames; }
|
uint32_t getMaxLeadFrames(){ return maxLeadFrames; }
|
||||||
void setMaxLeadFrames(uint32_t value){ maxLeadFrames = value; }
|
void setMaxLeadFrames(uint32_t value){ maxLeadFrames = value; }
|
||||||
|
void setAllowClientRomLoadRequest(bool value){ allowClientRomLoadReq = value; }
|
||||||
|
void setAllowClientStateLoadRequest(bool value){ allowClientStateLoadReq = value; }
|
||||||
|
|
||||||
void serverProcessMessage( NetPlayClient *client, void *msgBuf, size_t msgSize );
|
void serverProcessMessage( NetPlayClient *client, void *msgBuf, size_t msgSize );
|
||||||
|
|
||||||
|
@ -161,7 +163,9 @@ class NetPlayServer : public QTcpServer
|
||||||
uint32_t cycleCounter = 0;
|
uint32_t cycleCounter = 0;
|
||||||
uint32_t maxLeadFrames = 10u;
|
uint32_t maxLeadFrames = 10u;
|
||||||
uint32_t clientWaitCounter = 0;
|
uint32_t clientWaitCounter = 0;
|
||||||
|
uint32_t inputFrameCount = 0;
|
||||||
bool allowClientRomLoadReq = true;
|
bool allowClientRomLoadReq = true;
|
||||||
|
bool allowClientStateLoadReq = true;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
signals:
|
signals:
|
||||||
|
@ -203,12 +207,18 @@ class NetPlayClient : public QObject
|
||||||
int readMessages( void (*msgCallback)( void *userData, void *msgBuf, size_t msgSize ), void *userData );
|
int readMessages( void (*msgCallback)( void *userData, void *msgBuf, size_t msgSize ), void *userData );
|
||||||
void clientProcessMessage( void *msgBuf, size_t msgSize );
|
void clientProcessMessage( void *msgBuf, size_t msgSize );
|
||||||
|
|
||||||
size_t inputAvailable(void)
|
bool inputAvailable(void)
|
||||||
{
|
{
|
||||||
FCEU::autoScopedLock alock(inputMtx);
|
FCEU::autoScopedLock alock(inputMtx);
|
||||||
return !input.empty();
|
return !input.empty();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
size_t inputAvailableCount(void)
|
||||||
|
{
|
||||||
|
FCEU::autoScopedLock alock(inputMtx);
|
||||||
|
return input.size();
|
||||||
|
};
|
||||||
|
|
||||||
void pushBackInput( NetPlayFrameInput &in )
|
void pushBackInput( NetPlayFrameInput &in )
|
||||||
{
|
{
|
||||||
FCEU::autoScopedLock alock(inputMtx);
|
FCEU::autoScopedLock alock(inputMtx);
|
||||||
|
@ -264,6 +274,8 @@ class NetPlayClient : public QObject
|
||||||
bool syncOk = false;
|
bool syncOk = false;
|
||||||
unsigned int currentFrame = 0;
|
unsigned int currentFrame = 0;
|
||||||
unsigned int readyFrame = 0;
|
unsigned int readyFrame = 0;
|
||||||
|
unsigned int catchUpThreshold = 10;
|
||||||
|
unsigned int tailTarget = 3;
|
||||||
uint8_t gpData[4];
|
uint8_t gpData[4];
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -319,6 +331,9 @@ protected:
|
||||||
QComboBox *playerRoleBox;
|
QComboBox *playerRoleBox;
|
||||||
QLineEdit *passwordEntry;
|
QLineEdit *passwordEntry;
|
||||||
QCheckBox *passwordRequiredCBox;
|
QCheckBox *passwordRequiredCBox;
|
||||||
|
QSpinBox *frameLeadSpinBox;
|
||||||
|
QCheckBox *allowClientRomReqCBox;
|
||||||
|
QCheckBox *allowClientStateReqCBox;
|
||||||
|
|
||||||
static NetPlayHostDialog* instance;
|
static NetPlayHostDialog* instance;
|
||||||
|
|
||||||
|
|
|
@ -252,9 +252,10 @@ struct netPlayRunFrameReq
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
uint32_t frameNum;
|
uint32_t frameNum;
|
||||||
uint8_t ctrlState[4];
|
uint8_t ctrlState[4];
|
||||||
|
uint8_t catchUpThreshold;
|
||||||
|
|
||||||
netPlayRunFrameReq(void)
|
netPlayRunFrameReq(void)
|
||||||
: hdr(NETPLAY_RUN_FRAME_REQ, sizeof(netPlayRunFrameReq)), flags(0), frameNum(0)
|
: hdr(NETPLAY_RUN_FRAME_REQ, sizeof(netPlayRunFrameReq)), flags(0), frameNum(0), catchUpThreshold(10)
|
||||||
{
|
{
|
||||||
memset( ctrlState, 0, sizeof(ctrlState) );
|
memset( ctrlState, 0, sizeof(ctrlState) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,8 @@ bool useIntFrameRate = false;
|
||||||
static double frmRateAdjRatio = 1.000000f; // Frame Rate Adjustment Ratio
|
static double frmRateAdjRatio = 1.000000f; // Frame Rate Adjustment Ratio
|
||||||
extern bool turbo;
|
extern bool turbo;
|
||||||
|
|
||||||
|
int NetPlayThrottleControl();
|
||||||
|
|
||||||
double getHighPrecTimeStamp(void)
|
double getHighPrecTimeStamp(void)
|
||||||
{
|
{
|
||||||
double t;
|
double t;
|
||||||
|
@ -373,6 +375,7 @@ SpeedThrottle(void)
|
||||||
// If Emulator is paused, don't waste CPU cycles spinning on nothing.
|
// If Emulator is paused, don't waste CPU cycles spinning on nothing.
|
||||||
if ( !isEmuPaused && ((g_fpsScale >= 32) || turboActive) )
|
if ( !isEmuPaused && ((g_fpsScale >= 32) || turboActive) )
|
||||||
{
|
{
|
||||||
|
//printf("Skip Wait\n");
|
||||||
return 0; /* Done waiting */
|
return 0; /* Done waiting */
|
||||||
}
|
}
|
||||||
FCEU::timeStampRecord cur_time, idleStart, time_left;
|
FCEU::timeStampRecord cur_time, idleStart, time_left;
|
||||||
|
@ -502,6 +505,8 @@ SpeedThrottle(void)
|
||||||
//printf("Frame Delta: %f us min:%f max:%f \n", frameDelta * 1e6, frameDeltaMin * 1e6, frameDeltaMax * 1e6 );
|
//printf("Frame Delta: %f us min:%f max:%f \n", frameDelta * 1e6, frameDeltaMin * 1e6, frameDeltaMax * 1e6 );
|
||||||
//printf("Frame Sleep Time: %f Target Error: %f us\n", time_left * 1e6, (cur_time - Nexttime) * 1e6 );
|
//printf("Frame Sleep Time: %f Target Error: %f us\n", time_left * 1e6, (cur_time - Nexttime) * 1e6 );
|
||||||
}
|
}
|
||||||
|
NetPlayThrottleControl();
|
||||||
|
|
||||||
Lasttime = Nexttime;
|
Lasttime = Nexttime;
|
||||||
Nexttime = Lasttime + DesiredFrameTime;
|
Nexttime = Lasttime + DesiredFrameTime;
|
||||||
Latetime = Nexttime + HalfFrameTime;
|
Latetime = Nexttime + HalfFrameTime;
|
||||||
|
@ -512,6 +517,7 @@ SpeedThrottle(void)
|
||||||
Nexttime = Lasttime + DesiredFrameTime;
|
Nexttime = Lasttime + DesiredFrameTime;
|
||||||
Latetime = Nexttime + HalfFrameTime;
|
Latetime = Nexttime + HalfFrameTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0; /* Done waiting */
|
return 0; /* Done waiting */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,6 +576,64 @@ int CustomEmulationSpeed(int spdPercent)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int NetPlayThrottleControl()
|
||||||
|
{
|
||||||
|
NetPlayClient *client = NetPlayClient::GetInstance();
|
||||||
|
|
||||||
|
if (client)
|
||||||
|
{
|
||||||
|
uint32_t inputAvailCount = client->inputAvailableCount();
|
||||||
|
const uint32_t tailTarget = client->tailTarget;
|
||||||
|
|
||||||
|
double targetDelta = static_cast<double>( static_cast<intptr_t>(inputAvailCount) - static_cast<intptr_t>(tailTarget) );
|
||||||
|
|
||||||
|
// Simple linear FPS scaling adjustment based on target error.
|
||||||
|
constexpr double speedUpSlope = (0.05) / (10.0);
|
||||||
|
double newScale = 1.0 + (targetDelta * speedUpSlope);
|
||||||
|
|
||||||
|
if (newScale != g_fpsScale)
|
||||||
|
{
|
||||||
|
double hz;
|
||||||
|
int32_t fps = FCEUI_GetDesiredFPS(); // Do >> 24 to get in Hz
|
||||||
|
int32_t T;
|
||||||
|
|
||||||
|
g_fpsScale = newScale;
|
||||||
|
|
||||||
|
hz = ( ((double)fps) / 16777216.0 );
|
||||||
|
|
||||||
|
desired_frametime = 1.0 / ( hz * g_fpsScale );
|
||||||
|
|
||||||
|
if ( useIntFrameRate )
|
||||||
|
{
|
||||||
|
hz = (double)( (int)(hz) );
|
||||||
|
|
||||||
|
frmRateAdjRatio = (1.0 / ( hz * g_fpsScale )) / desired_frametime;
|
||||||
|
|
||||||
|
//printf("frameAdjRatio:%f \n", frmRateAdjRatio );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
frmRateAdjRatio = 1.000000f;
|
||||||
|
}
|
||||||
|
desired_frametime = 1.0 / ( hz * g_fpsScale );
|
||||||
|
desired_frameRate = ( hz * g_fpsScale );
|
||||||
|
baseframeRate = hz;
|
||||||
|
|
||||||
|
T = (int32_t)( desired_frametime * 1000.0 );
|
||||||
|
|
||||||
|
if ( T < 0 ) T = 1;
|
||||||
|
|
||||||
|
DesiredFrameTime.fromSeconds( desired_frametime );
|
||||||
|
HalfFrameTime = DesiredFrameTime / 2;
|
||||||
|
QuarterFrameTime = DesiredFrameTime / 4;
|
||||||
|
DoubleFrameTime = DesiredFrameTime * 2;
|
||||||
|
}
|
||||||
|
//printf("NetPlayCPUThrottle: %f %f %f Target:%u InputAvail:%u\n", newScale, desired_frameRate, targetDelta, tailTarget, inputAvailCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the emulation speed throttling to a specific value.
|
* Set the emulation speed throttling to a specific value.
|
||||||
|
|
Loading…
Reference in New Issue