diff --git a/driver.c b/driver.c
index 2796c27ee9..17369e1968 100644
--- a/driver.c
+++ b/driver.c
@@ -444,7 +444,7 @@ void init_audio(void)
static void compute_audio_buffer_statistics(void)
{
unsigned samples = min(g_extern.measure_data.buffer_free_samples_count, AUDIO_BUFFER_FREE_SAMPLES_COUNT);
- if (!samples)
+ if (samples < 2)
return;
uint64_t accum = 0;
@@ -460,7 +460,7 @@ static void compute_audio_buffer_statistics(void)
accum_var += diff * diff;
}
- unsigned stddev = (unsigned)sqrtf((float)accum_var / samples);
+ unsigned stddev = (unsigned)sqrt((double)accum_var / (samples - 1));
float avg_filled = 1.0f - (float)avg / g_extern.audio_data.driver_buffer_size;
float deviation = (float)stddev / g_extern.audio_data.driver_buffer_size;
@@ -487,27 +487,35 @@ static void compute_audio_buffer_statistics(void)
static void compute_monitor_fps_statistics(void)
{
- unsigned samples = min(g_extern.measure_data.fps_samples_count, MEASURE_FPS_SAMPLES_COUNT);
- if (!samples)
+ unsigned samples = min(g_extern.measure_data.frame_time_samples_count,
+ MEASURE_FRAME_TIME_SAMPLES_COUNT);
+
+ if (samples < 2)
return;
- // Measure statistics on frame time, *not* FPS.
- double accum = 0.0;
+ // Measure statistics on frame time (microsecs), *not* FPS.
+ rarch_time_t accum = 0;
for (unsigned i = 0; i < samples; i++)
- accum += 1.0 / g_extern.measure_data.fps_samples[i];
+ accum += g_extern.measure_data.frame_time_samples[i];
- double avg = accum / samples;
- double accum_var = 0.0;
+ rarch_time_t avg = accum / samples;
+ rarch_time_t accum_var = 0;
for (unsigned i = 0; i < samples; i++)
{
- double diff = avg - 1.0 / g_extern.measure_data.fps_samples[i];
+ rarch_time_t diff = avg - g_extern.measure_data.frame_time_samples[i];
accum_var += diff * diff;
}
- double stddev = sqrt(accum_var / samples);
+ double stddev = sqrt((double)accum_var / (samples - 1));
+ double avg_fps = 1000000.0 / avg;
+ double max_stddev_fps = 1000000.0 / (avg - stddev);
+ double stddev_fps = max_stddev_fps - avg_fps;
+ double sigma_deviation = (g_settings.video.refresh_rate - avg_fps) / stddev_fps;
- RARCH_LOG("Average monitor FPS: %.6f FPS. Standard deviation: %.6f FPS.\n",
- 1.0 / avg, 1.0 / (avg - stddev) - 1.0 / avg);
+ RARCH_LOG("Average monitor Hz: %.6f Hz. Standard deviation: %.6f Hz (%.3f %% deviation, based on %u last samples).\n",
+ avg_fps, stddev_fps, 100.0 * stddev_fps / avg_fps, samples);
+ RARCH_LOG("Configured monitor FPS %.6f Hz deviates %.3f sigma from average.\n",
+ g_settings.video.refresh_rate, sigma_deviation);
}
void uninit_audio(void)
@@ -837,7 +845,7 @@ void init_video_input(void)
}
#endif
- g_extern.measure_data.fps_samples_count = 0;
+ g_extern.measure_data.frame_time_samples_count = 0;
}
void uninit_video_input(void)
diff --git a/general.h b/general.h
index 4af006469d..6b5272dc9c 100644
--- a/general.h
+++ b/general.h
@@ -31,6 +31,7 @@
#include "cheats.h"
#include "audio/ext/rarch_dsp.h"
#include "compat/strl.h"
+#include "performance.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -412,9 +413,9 @@ struct global
unsigned buffer_free_samples[AUDIO_BUFFER_FREE_SAMPLES_COUNT];
uint64_t buffer_free_samples_count;
-#define MEASURE_FPS_SAMPLES_COUNT (8 * 1024)
- float fps_samples[MEASURE_FPS_SAMPLES_COUNT];
- uint64_t fps_samples_count;
+#define MEASURE_FRAME_TIME_SAMPLES_COUNT 256
+ rarch_time_t frame_time_samples[MEASURE_FRAME_TIME_SAMPLES_COUNT];
+ uint64_t frame_time_samples_count;
} measure_data;
struct
diff --git a/gfx/gfx_common.c b/gfx/gfx_common.c
index 826738221d..aace2f90a1 100644
--- a/gfx/gfx_common.c
+++ b/gfx/gfx_common.c
@@ -14,94 +14,37 @@
* If not, see .
*/
-#if defined(_MSC_VER) && !defined(_XBOX)
-#pragma comment(lib, "winmm")
-#endif
-
#include "gfx_common.h"
#include "../general.h"
+#include "../performance.h"
-#ifndef _MSC_VER
-#include
-#else
-#ifndef _XBOX
-#include
-#include
-#endif
-#endif
-
-#if defined(__PSL1GHT__)
-#include
-#elif defined(__CELLOS_LV2__)
-#include
-#endif
-
-#ifdef GEKKO
-#include
-#endif
-
-#ifdef __linux__
-#include
-#include
-#include
-#endif
-
-#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(_MSC_VER) || defined(GEKKO)
-static int gettimeofday2(struct timeval *val, void *dummy)
+static float time_to_fps(rarch_time_t last_time, rarch_time_t new_time, int frames)
{
- (void)dummy;
-#if defined(_MSC_VER) && !defined(_XBOX360)
- DWORD msec = timeGetTime();
-#elif defined(_XBOX360)
- DWORD msec = GetTickCount();
-#endif
-
-#if defined(__CELLOS_LV2__)
- uint64_t usec = sys_time_get_system_time();
-#elif defined(GEKKO)
- uint64_t usec = ticks_to_microsecs(gettime());
-#else
- uint64_t usec = msec * 1000;
-#endif
-
- val->tv_sec = usec / 1000000;
- val->tv_usec = usec % 1000000;
- return 0;
-}
-
-// GEKKO has gettimeofday, but it's not accurate enough for calculating FPS, so hack around it
-#define gettimeofday gettimeofday2
-#endif
-
-static float tv_to_fps(const struct timeval *tv, const struct timeval *new_tv, int frames)
-{
- float time = new_tv->tv_sec - tv->tv_sec + (new_tv->tv_usec - tv->tv_usec) / 1000000.0;
- return frames/time;
+ return (1000000.0f * frames) / (new_time - last_time);
}
bool gfx_get_fps(char *buf, size_t size, bool always_write)
{
- static struct timeval tv;
+ static rarch_time_t time;
static float last_fps;
- struct timeval new_tv;
bool ret = false;
if (g_extern.frame_count == 0)
{
- gettimeofday(&tv, NULL);
+ time = rarch_get_time_usec();
snprintf(buf, size, "%s", g_extern.title_buf);
ret = true;
}
else if ((g_extern.frame_count % 180) == 0)
{
- gettimeofday(&new_tv, NULL);
- struct timeval tmp_tv = tv;
- tv = new_tv;
+ rarch_time_t new_time = rarch_get_time_usec();
+ last_fps = time_to_fps(time, new_time, 180);
- last_fps = tv_to_fps(&tmp_tv, &new_tv, 180);
+ unsigned write_index = g_extern.measure_data.frame_time_samples_count++ &
+ (MEASURE_FRAME_TIME_SAMPLES_COUNT - 1);
+ g_extern.measure_data.frame_time_samples[write_index] = (new_time - time) / 180;
- unsigned write_index = g_extern.measure_data.fps_samples_count++ & (MEASURE_FPS_SAMPLES_COUNT - 1);
- g_extern.measure_data.fps_samples[write_index] = last_fps;
+ time = new_time;
#ifdef RARCH_CONSOLE
snprintf(buf, size, "FPS: %6.1f || Frames: %d", last_fps, g_extern.frame_count);
@@ -138,7 +81,10 @@ static dylib_t dwmlib = NULL;
static void gfx_dwm_shutdown(void)
{
if (dwmlib)
+ {
dylib_close(dwmlib);
+ dwmlib = NULL;
+ }
}
void gfx_set_dwm(void)
diff --git a/performance.c b/performance.c
index 9f4e6b1863..278f581cfb 100644
--- a/performance.c
+++ b/performance.c
@@ -15,24 +15,50 @@
*/
#include "performance.h"
+#include "general.h"
+
+#if defined(_MSC_VER) && !defined(_XBOX)
+#pragma comment(lib, "winmm")
+#endif
#ifdef ANDROID
#include "android/native/jni/cpufeatures.h"
#endif
-#ifdef PERF_TEST
+#if !defined(_WIN32) && !defined(RARCH_CONSOLE)
+#include
+#endif
#if defined(__CELLOS_LV2__) || defined(GEKKO)
#ifndef _PPU_INTRINSICS_H
#include
#endif
+#elif defined(_WIN32) && !defined(_XBOX)
+#include
#elif defined(_XBOX360)
#include
-#elif defined(__linux__)
-#include
+#elif defined(_POSIX_MONOTONIC_CLOCK) || defined(ANDROID)
+// POSIX_MONOTONIC_CLOCK is not being defined in Android headers despite support being present.
+#include
#endif
+#if defined(__PSL1GHT__)
+#include
+#elif defined(__CELLOS_LV2__)
+#include
+#endif
+#ifdef GEKKO
+#include
+#endif
+
+// OSX specific. OSX lacks clock_gettime().
+#ifdef __MACH__
+#include
+#include
+#endif
+
+#ifdef PERF_TEST
#define MAX_COUNTERS 64
static struct rarch_perf_counter *perf_counters[MAX_COUNTERS];
static unsigned perf_ptr;
@@ -82,7 +108,7 @@ rarch_perf_tick_t rarch_get_perf_counter(void)
time = (rarch_perf_tick_t)a | ((rarch_perf_tick_t)d << 32);
#endif
-#elif defined(__ARM_ARCH_6__) || defined(ANDROID)
+#elif defined(__ARM_ARCH_6__)
asm volatile( "mrc p15, 0, %0, c9, c13, 0" : "=r"(time) );
#elif defined(__CELLOS_LV2__) || defined(GEKKO) || defined(_XBOX360)
time = __mftb();
@@ -92,6 +118,33 @@ rarch_perf_tick_t rarch_get_perf_counter(void)
}
#endif
+rarch_time_t rarch_get_time_usec(void)
+{
+#if defined(_WIN32) && !defined(_XBOX360)
+ return timeGetTime() * INT64_C(1000); // FIXME: Need more accurate measurement, i.e. QueryPerformanceCounter.
+#elif defined(_XBOX360)
+ return GetTickCount() * INT64_C(1000); // FIXME: Need more accurate measurement.
+#elif defined(__CELLOS_LV2__)
+ return sys_time_get_system_time();
+#elif defined(GEKKO)
+ return ticks_to_microsecs(gettime());
+#elif defined(__MACH__) // OSX doesn't have clock_gettime ...
+ clock_serv_t cclock;
+ mach_timespec_t mts;
+ host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+ clock_get_time(cclock, &mts);
+ mach_port_deallocate(mach_task_self(), cclock);
+ return mts.tv_sec * INT64_C(1000000) + mts.tv_nsec / 1000;
+#elif defined(_POSIX_MONOTONIC_CLOCK) || defined(ANDROID)
+ struct timespec tv;
+ if (clock_gettime(CLOCK_MONOTONIC, &tv) < 0)
+ return 0;
+ return tv.tv_sec * INT64_C(1000000) + tv.tv_nsec / 1000;
+#else
+#error "Your platform does not have a timer function implemented in rarch_get_time_usec(). Cannot continue."
+#endif
+}
+
#if defined(__x86_64__) || defined(__i386__) || defined(__i486__) || defined(__i686__)
#define CPU_X86
#endif
diff --git a/performance.h b/performance.h
index 02259c2c8d..01831faaed 100644
--- a/performance.h
+++ b/performance.h
@@ -17,14 +17,14 @@
#ifndef _RARCH_PERF_H
#define _RARCH_PERF_H
-#include "general.h"
-
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
+#include "boolean.h"
#include
typedef unsigned long long rarch_perf_tick_t;
+typedef int64_t rarch_time_t;
typedef struct rarch_perf_counter
{
@@ -37,6 +37,7 @@ typedef struct rarch_perf_counter
} rarch_perf_counter_t;
rarch_perf_tick_t rarch_get_perf_counter(void);
+rarch_time_t rarch_get_time_usec(void);
void rarch_perf_register(struct rarch_perf_counter *perf);
void rarch_perf_log(void);