diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index bfead09d..4b14580e 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -1,5 +1,14 @@ // GameApp.cpp // +#ifdef __linux__ +#include +#include +#include +#include +#include +#include +#endif + #include #include #include @@ -99,7 +108,9 @@ consoleWin_t::consoleWin_t(QWidget *parent) gameTimer->start( 8 ); // 120hz emulatorThread->start(); + //emulatorThread->setPriority( QThread::TimeCriticalPriority ); + //setPriority( QThread::TimeCriticalPriority ); } consoleWin_t::~consoleWin_t(void) @@ -1770,6 +1781,36 @@ void consoleWin_t::aboutQt(void) return; } +void consoleWin_t::setPriority( QThread::Priority priority_req ) +{ +#ifdef __linux__ + struct sched_param p; + int minPrio, maxPrio; + + minPrio = sched_get_priority_min( SCHED_FIFO ); + maxPrio = sched_get_priority_max( SCHED_FIFO ); + + p.sched_priority = maxPrio; + + if ( ::setpriority( PRIO_PROCESS, getpid(), -20 ) ) + { + perror("Qt Window thread setpriority error: "); + } + printf("sched_getscheduler(): %i \n", sched_getscheduler( getpid() ) ); + printf("sched_get_priority_min(SCHED_FIFO): %i \n", minPrio ); + printf("sched_get_priority_max(SCHED_FIFO): %i \n", maxPrio ); + + printf("setpriority(): %i \n", ::getpriority( PRIO_PROCESS, getpid() ) ); + + if ( sched_setscheduler( getpid(), SCHED_FIFO, &p ) ) + { + perror("Qt Window thread sched_setscheduler error:"); + } + printf("sched_getscheduler(): %i \n", sched_getscheduler( getpid() ) ); + +#endif +} + void consoleWin_t::syncActionConfig( QAction *act, const char *property ) { if ( act->isCheckable() ) @@ -1821,11 +1862,80 @@ void consoleWin_t::updatePeriodic(void) return; } +emulatorThread_t::emulatorThread_t(void) +{ + +} + +#ifdef __linux__ +#ifndef SYS_gettid +#error "SYS_gettid unavailable on this system" +#endif + +#define gettid() ((pid_t)syscall(SYS_gettid)) +#endif + + +void emulatorThread_t::setPriority( QThread::Priority priority_req ) +{ + printf("New Priority: %i \n", priority_req ); + printf("Old Priority: %i \n", priority() ); + + QThread::setPriority( priority_req ); + + printf("Set Priority: %i \n", priority() ); + +#ifdef __linux__ + struct sched_param p; + int oldPolicy, newPolicy, minPrio, maxPrio; + pthread_t self; + + self = pthread_self(); + newPolicy = SCHED_FIFO; + + minPrio = sched_get_priority_min( SCHED_FIFO ); + maxPrio = sched_get_priority_max( SCHED_FIFO ); + + pthread_getschedparam( self, &oldPolicy, &p ); + + printf("pthread_getschedparam(): %i, %i \n", oldPolicy, p.sched_priority ); + + p.sched_priority = maxPrio; + + if ( ::pthread_setschedparam( self, newPolicy, &p ) != 0 ) + { + perror("Emulator thread pthread_setschedparam error: "); + } + + if ( ::setpriority( PRIO_PROCESS, gettid(), -20 ) ) + { + perror("Emulator thread setpriority error: "); + } + printf("sched_getscheduler(): %i \n", sched_getscheduler( getpid() ) ); + printf("sched_get_priority_min(SCHED_FIFO): %i \n", minPrio ); + printf("sched_get_priority_max(SCHED_FIFO): %i \n", maxPrio ); + + printf("setpriority(): %i \n", ::getpriority( PRIO_PROCESS, getpid() ) ); + + //if ( sched_setscheduler( getpid(), SCHED_FIFO, &p ) ) + //{ + // perror("Emulator thread sched_setscheduler error:"); + //} + printf("sched_getscheduler(): %i \n", sched_getscheduler( getpid() ) ); + + pthread_getschedparam( self, &oldPolicy, &p ); + + printf("pthread_getschedparam(): %i, %i \n", oldPolicy, p.sched_priority ); +#endif +} + void emulatorThread_t::run(void) { printf("Emulator Start\n"); nes_shm->runEmulator = 1; + setPriority( QThread::TimeCriticalPriority ); + while ( nes_shm->runEmulator ) { fceuWrapperUpdate(); diff --git a/src/drivers/Qt/ConsoleWindow.h b/src/drivers/Qt/ConsoleWindow.h index d883d779..643731cb 100644 --- a/src/drivers/Qt/ConsoleWindow.h +++ b/src/drivers/Qt/ConsoleWindow.h @@ -28,10 +28,18 @@ class emulatorThread_t : public QThread { Q_OBJECT - //public slots: + protected: void run( void ) override; + + public: + emulatorThread_t(void); + + void setPriority( QThread::Priority priority ); + + private: + signals: - void finished(); + void finished(); }; class consoleWin_t : public QMainWindow @@ -53,6 +61,8 @@ class consoleWin_t : public QMainWindow int showListSelectDialog( const char *title, std::vector &l ); + void setPriority( QThread::Priority priority_req ); + protected: QMenu *fileMenu; QMenu *optMenu; diff --git a/src/drivers/Qt/fceuWrapper.cpp b/src/drivers/Qt/fceuWrapper.cpp index 484ad880..6edcd4e5 100644 --- a/src/drivers/Qt/fceuWrapper.cpp +++ b/src/drivers/Qt/fceuWrapper.cpp @@ -958,13 +958,6 @@ FCEUD_Update(uint8 *XBuf, } else { - //if (!NoWaiting && (!(eoptions&EO_NOTHROTTLE) || FCEUI_EmulationPaused())) - //{ - // while (SpeedThrottle()) - // { - // FCEUD_UpdateInput(); - // } - //} if (XBuf && (inited&4)) { BlitScreen(XBuf); blitDone = 1; diff --git a/src/drivers/Qt/sdl-throttle.cpp b/src/drivers/Qt/sdl-throttle.cpp index 2ade8ae4..5d3e9833 100644 --- a/src/drivers/Qt/sdl-throttle.cpp +++ b/src/drivers/Qt/sdl-throttle.cpp @@ -4,16 +4,93 @@ #include "Qt/sdl.h" #include "Qt/throttle.h" +#if defined(__linux) || defined(__APPLE__) +#include +#endif + +#ifdef __linux__ +#include +#endif + 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) static const double Normal = 1.0; // 1x speed (around 60 fps on NTSC) -static uint64 Lasttime, Nexttime; +static uint32 frameLateCounter = 0; +static double Lasttime=0, Nexttime=0, Latetime=0; static double desired_frametime = (1.0 / 60.099823); +static double frameDeltaMin = 99999.0; +static double frameDeltaMax = 0.0; +static bool keepFrameTimeStats = true; static int InFrame = 0; double g_fpsScale = Normal; // used by sdl.cpp bool MaxSpeed = false; +double getHighPrecTimeStamp(void) +{ +#if defined(__linux) || defined(__APPLE__) + struct timespec ts; + double t; + + clock_gettime( CLOCK_REALTIME, &ts ); + + t = (double)ts.tv_sec + (double)(ts.tv_nsec * 1.0e-9); +#else + double t; + + t = (double)SDL_GetTicks(); + + t = t * 1e-3; +#endif + + return t; +} + +#ifdef __linux__ +static char useTimerFD = 1; +static int timerfd = -1; + +static void setTimer( double hz ) +{ + struct itimerspec ispec; + + if ( !useTimerFD ) + { + if ( timerfd != -1 ) + { + ::close( timerfd ); timerfd = -1; + } + return; + } + + if ( timerfd == -1 ) + { + timerfd = timerfd_create( CLOCK_REALTIME, 0 ); + + if ( timerfd == -1 ) + { + perror("timerfd_create failed: "); + return; + } + } + ispec.it_interval.tv_sec = 0; + ispec.it_interval.tv_nsec = (long)( 1.0e9 / hz ); + ispec.it_value.tv_sec = ispec.it_interval.tv_sec; + ispec.it_value.tv_nsec = ispec.it_interval.tv_nsec; + + if ( timerfd_settime( timerfd, 0, &ispec, NULL ) == -1 ) + { + perror("timerfd_settime failed: "); + } + + printf("Timer Set: %li ns\n", ispec.it_value.tv_nsec ); + + Lasttime = getHighPrecTimeStamp(); + Nexttime = Lasttime + desired_frametime; + Latetime = Nexttime + desired_frametime; +} +#endif + /* LOGMUL = exp(log(2) / 3) * * This gives us a value such that if we do x*=LOGMUL three times, @@ -28,7 +105,7 @@ bool MaxSpeed = false; * Refreshes the FPS throttling variables. */ void -RefreshThrottleFPS() +RefreshThrottleFPS(void) { double hz; int32_t fps = FCEUI_GetDesiredFPS(); // Do >> 24 to get in Hz @@ -47,37 +124,67 @@ RefreshThrottleFPS() Lasttime=0; Nexttime=0; InFrame=0; + +#ifdef __linux__ + setTimer( hz * g_fpsScale ); +#endif + +} + +int highPrecSleep( double timeSeconds ) +{ + int ret = 0; +#if defined(__linux) || defined(__APPLE__) + struct timespec req, rem; + + req.tv_sec = (long)timeSeconds; + req.tv_nsec = (long)((timeSeconds - (double)req.tv_sec) * 1e9); + + ret = nanosleep( &req, &rem ); +#else + SDL_Delay( (long)(time_left * 1e3) ); +#endif + return ret; } /** * Perform FPS speed throttling by delaying until the next time slot. */ int -SpeedThrottle() +SpeedThrottle(void) { if (g_fpsScale >= 32) { return 0; /* Done waiting */ } - uint64 time_left; - uint64 cur_time; + double time_left; + double cur_time; + double frame_time = desired_frametime; + double quarterFrame = 0.250 * frame_time; - if (!Lasttime) + cur_time = getHighPrecTimeStamp(); + + if (Lasttime < 1.0) { - Lasttime = SDL_GetTicks(); + Lasttime = cur_time; + Latetime = Lasttime + frame_time; } if (!InFrame) { InFrame = 1; - Nexttime = Lasttime + desired_frametime * 1000; + Nexttime = Lasttime + frame_time; + Latetime = Nexttime + frame_time; } - cur_time = SDL_GetTicks(); - if(cur_time >= Nexttime) + if (cur_time >= Nexttime) + { time_left = 0; + } else + { time_left = Nexttime - cur_time; + } if (time_left > 50) { @@ -93,16 +200,81 @@ SpeedThrottle() //fprintf(stderr, "attempting to sleep %Ld ms, frame complete=%s\n", // time_left, InFrame?"no":"yes"); +#ifdef __linux__ + if ( timerfd != -1 ) + { + uint64_t val; + + if ( read( timerfd, &val, sizeof(val) ) > 0 ) + { + if ( val > 1 ) + { + frameLateCounter += (val - 1); + //printf("Late Frame: %u \n", frameLateCounter); + } + } + } + else if ( time_left > 0 ) + { + highPrecSleep( time_left ); + } + else + { + if ( cur_time >= Latetime ) + { + frameLateCounter++; + //printf("Late Frame: %u - %llu ms\n", frameLateCounter, cur_time - Latetime); + } + } +#else if ( time_left > 0 ) { - SDL_Delay(time_left); + highPrecSleep( time_left ); } - - if (!InFrame) + else { - Lasttime = SDL_GetTicks(); + if ( cur_time >= Latetime ) + { + frameLateCounter++; + //printf("Late Frame: %u - %llu ms\n", frameLateCounter, cur_time - Latetime); + } + } +#endif + + cur_time = getHighPrecTimeStamp(); + + if ( cur_time >= (Nexttime - quarterFrame) ) + { + if ( keepFrameTimeStats ) + { + double frameDelta; + + frameDelta = (cur_time - Lasttime); + + if ( frameDelta < frameDeltaMin ) + { + frameDeltaMin = frameDelta; + } + if ( frameDelta > frameDeltaMax ) + { + frameDeltaMax = frameDelta; + } + //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 ); + } + Lasttime = Nexttime; + Nexttime = Lasttime + frame_time; + Latetime = Nexttime + frame_time; + + if ( cur_time >= Nexttime ) + { + Lasttime = cur_time; + Nexttime = Lasttime + frame_time; + Latetime = Nexttime + frame_time; + } return 0; /* Done waiting */ } + return 1; /* Must still wait some more */ } @@ -113,7 +285,7 @@ void IncreaseEmulationSpeed(void) { g_fpsScale *= LOGMUL; - if(g_fpsScale > Fastest) g_fpsScale = Fastest; + if (g_fpsScale > Fastest) g_fpsScale = Fastest; RefreshThrottleFPS();