Moved FCEU emulation to its own thread for better timing control.
This commit is contained in:
parent
c496c0f281
commit
acc02ee98e
|
@ -7,6 +7,7 @@
|
|||
#include "Qt/GameSoundConf.h"
|
||||
#include "Qt/fceuWrapper.h"
|
||||
#include "Qt/keyscan.h"
|
||||
#include "Qt/nes_shm.h"
|
||||
|
||||
gameWin_t::gameWin_t(QWidget *parent)
|
||||
: QMainWindow( parent )
|
||||
|
@ -19,25 +20,45 @@ gameWin_t::gameWin_t(QWidget *parent)
|
|||
|
||||
setCentralWidget(viewport);
|
||||
|
||||
gameTimer = new QTimer( this );
|
||||
gameTimer = new QTimer( this );
|
||||
gameThread = new QThread( this );
|
||||
worker = new gameWorkerThread_t();
|
||||
mutex = new QMutex( QMutex::NonRecursive );
|
||||
|
||||
worker->moveToThread(gameThread);
|
||||
connect(gameThread, &QThread::finished, worker, &QObject::deleteLater);
|
||||
connect(gameThread, SIGNAL (started()), worker, SLOT( runEmulator() ));
|
||||
connect(worker, SIGNAL (finished()), gameThread, SLOT (quit()));
|
||||
connect(worker, SIGNAL (finished()), worker, SLOT (deleteLater()));
|
||||
|
||||
connect( gameTimer, &QTimer::timeout, this, &gameWin_t::runGameFrame );
|
||||
|
||||
gameTimer->setTimerType( Qt::PreciseTimer );
|
||||
gameTimer->start( 16 );
|
||||
|
||||
gameThread->start();
|
||||
|
||||
gamePadConfWin = NULL;
|
||||
}
|
||||
|
||||
gameWin_t::~gameWin_t(void)
|
||||
{
|
||||
nes_shm->runEmulator = 0;
|
||||
|
||||
if ( gamePadConfWin != NULL )
|
||||
{
|
||||
gamePadConfWin->closeWindow();
|
||||
}
|
||||
fceuWrapperLock();
|
||||
fceuWrapperClose();
|
||||
fceuWrapperUnLock();
|
||||
|
||||
//printf("Thread Finished: %i \n", gameThread->isFinished() );
|
||||
gameThread->exit(0);
|
||||
gameThread->wait();
|
||||
|
||||
delete viewport;
|
||||
delete mutex;
|
||||
}
|
||||
|
||||
void gameWin_t::setCyclePeriodms( int ms )
|
||||
|
@ -142,7 +163,11 @@ void gameWin_t::createMainMenu(void)
|
|||
//---------------------------------------------------------------------------
|
||||
void gameWin_t::closeApp(void)
|
||||
{
|
||||
nes_shm->runEmulator = 0;
|
||||
|
||||
fceuWrapperLock();
|
||||
fceuWrapperClose();
|
||||
fceuWrapperUnLock();
|
||||
|
||||
// LoadGame() checks for an IP and if it finds one begins a network session
|
||||
// clear the NetworkIP field so this doesn't happen unintentionally
|
||||
|
@ -197,15 +222,20 @@ void gameWin_t::openROMFile(void)
|
|||
qDebug() << "selected file path : " << filename.toUtf8();
|
||||
|
||||
g_config->setOption ("SDL.LastOpenFile", filename.toStdString().c_str() );
|
||||
|
||||
fceuWrapperLock();
|
||||
CloseGame ();
|
||||
LoadGame ( filename.toStdString().c_str() );
|
||||
fceuWrapperUnLock();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void gameWin_t::closeROMCB(void)
|
||||
{
|
||||
fceuWrapperLock();
|
||||
CloseGame();
|
||||
fceuWrapperUnLock();
|
||||
}
|
||||
|
||||
void gameWin_t::openGamePadConfWin(void)
|
||||
|
@ -258,9 +288,20 @@ void gameWin_t::runGameFrame(void)
|
|||
//t = (double)ts.tv_sec + (double)(ts.tv_nsec * 1.0e-9);
|
||||
//printf("Run Frame %f\n", t);
|
||||
|
||||
fceuWrapperUpdate();
|
||||
|
||||
viewport->repaint();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void gameWorkerThread_t::runEmulator(void)
|
||||
{
|
||||
printf("Emulator Start\n");
|
||||
nes_shm->runEmulator = 1;
|
||||
|
||||
while ( nes_shm->runEmulator )
|
||||
{
|
||||
fceuWrapperUpdate();
|
||||
}
|
||||
printf("Emulator Exit\n");
|
||||
emit finished();
|
||||
}
|
||||
|
|
|
@ -14,11 +14,23 @@
|
|||
#include <QHBoxLayout>
|
||||
#include <QKeyEvent>
|
||||
#include <QTimer>
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
|
||||
#include "Qt/GameViewerGL.h"
|
||||
#include "Qt/GameViewerSDL.h"
|
||||
#include "Qt/GamePadConf.h"
|
||||
|
||||
class gameWorkerThread_t : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
void runEmulator( void );
|
||||
signals:
|
||||
void finished();
|
||||
};
|
||||
|
||||
class gameWin_t : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -32,6 +44,8 @@ class gameWin_t : public QMainWindow
|
|||
|
||||
void setCyclePeriodms( int ms );
|
||||
|
||||
QMutex *mutex;
|
||||
|
||||
protected:
|
||||
QMenu *fileMenu;
|
||||
QMenu *optMenu;
|
||||
|
@ -45,6 +59,8 @@ class gameWin_t : public QMainWindow
|
|||
QAction *aboutAct;
|
||||
|
||||
QTimer *gameTimer;
|
||||
QThread *gameThread;
|
||||
gameWorkerThread_t *worker;
|
||||
|
||||
GamePadConfDialog_t *gamePadConfWin;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "Qt/sdl-video.h"
|
||||
#include "Qt/nes_shm.h"
|
||||
#include "Qt/unix-netplay.h"
|
||||
#include "Qt/GameApp.h"
|
||||
|
||||
#include "common/cheat.h"
|
||||
#include "../../fceu.h"
|
||||
|
@ -55,6 +56,7 @@ static int inited = 0;
|
|||
static int noconfig=0;
|
||||
static int frameskip=0;
|
||||
static int periodic_saves = 0;
|
||||
static bool mutexLocked = 0;
|
||||
|
||||
extern double g_fpsScale;
|
||||
//*****************************************************************
|
||||
|
@ -711,13 +713,13 @@ FCEUD_Update(uint8 *XBuf,
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!NoWaiting && (!(eoptions&EO_NOTHROTTLE) || FCEUI_EmulationPaused()))
|
||||
{
|
||||
while (SpeedThrottle())
|
||||
{
|
||||
FCEUD_UpdateInput();
|
||||
}
|
||||
}
|
||||
//if (!NoWaiting && (!(eoptions&EO_NOTHROTTLE) || FCEUI_EmulationPaused()))
|
||||
//{
|
||||
// while (SpeedThrottle())
|
||||
// {
|
||||
// FCEUD_UpdateInput();
|
||||
// }
|
||||
//}
|
||||
if (XBuf && (inited&4))
|
||||
{
|
||||
BlitScreen(XBuf); blitDone = 1;
|
||||
|
@ -772,13 +774,64 @@ static void DoFun(int frameskip, int periodic_saves)
|
|||
}
|
||||
}
|
||||
|
||||
void fceuWrapperLock(void)
|
||||
{
|
||||
gameWindow->mutex->lock();
|
||||
mutexLocked = 1;
|
||||
}
|
||||
|
||||
bool fceuWrapperTryLock(int timeout)
|
||||
{
|
||||
bool lockAcq;
|
||||
|
||||
lockAcq = gameWindow->mutex->tryLock( timeout );
|
||||
|
||||
if ( lockAcq )
|
||||
{
|
||||
mutexLocked = 1;
|
||||
}
|
||||
return lockAcq;
|
||||
}
|
||||
|
||||
void fceuWrapperUnLock(void)
|
||||
{
|
||||
if ( mutexLocked )
|
||||
{
|
||||
gameWindow->mutex->unlock();
|
||||
mutexLocked = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error: Mutex is Already UnLocked\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool fceuWrapperIsLocked(void)
|
||||
{
|
||||
return mutexLocked;
|
||||
}
|
||||
|
||||
int fceuWrapperUpdate( void )
|
||||
{
|
||||
fceuWrapperLock();
|
||||
|
||||
if (GameInfo)
|
||||
{
|
||||
DoFun(frameskip, periodic_saves);
|
||||
}
|
||||
|
||||
fceuWrapperUnLock();
|
||||
|
||||
while ( SpeedThrottle() )
|
||||
{
|
||||
FCEUD_UpdateInput();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fceuWrapperUnLock();
|
||||
|
||||
usleep( 100000 );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,4 +25,8 @@ int CloseGame(void);
|
|||
int fceuWrapperInit( int argc, char *argv[] );
|
||||
int fceuWrapperClose( void );
|
||||
int fceuWrapperUpdate( void );
|
||||
void fceuWrapperLock(void);
|
||||
bool fceuWrapperTryLock(int timeout);
|
||||
bool fceuWrapperIsLocked(void);
|
||||
void fceuWrapperUnLock(void);
|
||||
|
||||
|
|
|
@ -611,10 +611,10 @@ static void KeyboardCommands (void)
|
|||
|
||||
if (_keyonly (Hotkeys[HK_DECREASE_SPEED]))
|
||||
{
|
||||
DecreaseEmulationSpeed ();
|
||||
DecreaseEmulationSpeed();
|
||||
}
|
||||
|
||||
if (_keyonly (Hotkeys[HK_INCREASE_SPEED]))
|
||||
if (_keyonly(Hotkeys[HK_INCREASE_SPEED]))
|
||||
{
|
||||
IncreaseEmulationSpeed ();
|
||||
}
|
||||
|
|
|
@ -7,17 +7,24 @@ gameWin_t *gameWindow = NULL;
|
|||
|
||||
int main( int argc, char *argv[] )
|
||||
{
|
||||
int retval;
|
||||
QApplication app(argc, argv);
|
||||
|
||||
gameWindow = new gameWin_t();
|
||||
|
||||
fceuWrapperInit( argc, argv );
|
||||
|
||||
gameWindow = new gameWin_t();
|
||||
|
||||
gameWindow->resize( 512, 512 );
|
||||
gameWindow->show();
|
||||
|
||||
gameWindow->viewport->init();
|
||||
|
||||
return app.exec();
|
||||
retval = app.exec();
|
||||
|
||||
//printf("App Return: %i \n", retval );
|
||||
|
||||
delete gameWindow;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ struct nes_shm_t
|
|||
int nrow;
|
||||
int pitch;
|
||||
|
||||
char runEmulator;
|
||||
|
||||
// Pass Key Events back to QT Gui
|
||||
struct
|
||||
{
|
||||
|
|
|
@ -50,6 +50,7 @@ fillaudio(void *udata,
|
|||
uint8 *stream,
|
||||
int len)
|
||||
{
|
||||
char bufStarveDetected = 0;
|
||||
static int16_t sample = 0;
|
||||
int16 *tmps = (int16*)stream;
|
||||
len >>= 1;
|
||||
|
@ -65,8 +66,8 @@ fillaudio(void *udata,
|
|||
// Retain last known sample value, helps avoid clicking
|
||||
// noise when sound system is starved of audio data.
|
||||
//sample = 0;
|
||||
bufStarveDetected = 1;
|
||||
nes_shm->sndBuf.starveCounter++;
|
||||
//printf("Starve:%u\n", nes_shm->sndBuf.starveCounter );
|
||||
}
|
||||
|
||||
nes_shm->push_sound_sample( sample );
|
||||
|
@ -75,6 +76,10 @@ fillaudio(void *udata,
|
|||
tmps++;
|
||||
len--;
|
||||
}
|
||||
if ( bufStarveDetected )
|
||||
{
|
||||
//printf("Starve:%u\n", nes_shm->sndBuf.starveCounter );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "Qt/sdl.h"
|
||||
#include "Qt/throttle.h"
|
||||
#include "Qt/GameApp.h"
|
||||
#include "Qt/fceuWrapper.h"
|
||||
|
||||
static const double Slowest = 0.015625; // 1/64x speed (around 1 fps on NTSC)
|
||||
static const double Fastest = 32; // 32x speed (around 1920 fps on NTSC)
|
||||
|
@ -45,7 +46,7 @@ RefreshThrottleFPS()
|
|||
|
||||
//printf("FrameTime: %llu %llu %f %lf \n", fps, fps >> 24, hz, desired_frametime );
|
||||
|
||||
gameWindow->setCyclePeriodms( T );
|
||||
//gameWindow->setCyclePeriodms( T );
|
||||
|
||||
Lasttime=0;
|
||||
Nexttime=0;
|
||||
|
@ -58,19 +59,19 @@ RefreshThrottleFPS()
|
|||
int
|
||||
SpeedThrottle()
|
||||
{
|
||||
return 0;
|
||||
|
||||
if(g_fpsScale >= 32)
|
||||
if (g_fpsScale >= 32)
|
||||
{
|
||||
return 0; /* Done waiting */
|
||||
}
|
||||
uint64 time_left;
|
||||
uint64 cur_time;
|
||||
|
||||
if(!Lasttime)
|
||||
if (!Lasttime)
|
||||
{
|
||||
Lasttime = SDL_GetTicks();
|
||||
}
|
||||
|
||||
if(!InFrame)
|
||||
if (!InFrame)
|
||||
{
|
||||
InFrame = 1;
|
||||
Nexttime = Lasttime + desired_frametime * 1000;
|
||||
|
@ -89,17 +90,21 @@ SpeedThrottle()
|
|||
/* 50 ms wait gives us a 20 Hz responsetime which is nice. */
|
||||
}
|
||||
else
|
||||
{
|
||||
InFrame = 0;
|
||||
}
|
||||
|
||||
//fprintf(stderr, "attempting to sleep %Ld ms, frame complete=%s\n",
|
||||
// time_left, InFrame?"no":"yes");
|
||||
|
||||
if ( time_left > 0 )
|
||||
{
|
||||
//fceuWrapperUnLock();
|
||||
SDL_Delay(time_left);
|
||||
//fceuWrapperLock();
|
||||
}
|
||||
|
||||
if(!InFrame)
|
||||
if (!InFrame)
|
||||
{
|
||||
Lasttime = SDL_GetTicks();
|
||||
return 0; /* Done waiting */
|
||||
|
|
Loading…
Reference in New Issue