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);