Rework FPS measurement.

This commit is contained in:
Themaister 2013-02-05 09:41:10 +01:00
parent d80e0df9e9
commit 1df86a722a
5 changed files with 100 additions and 91 deletions

View File

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

View File

@ -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

View File

@ -14,94 +14,37 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#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 <sys/time.h>
#else
#ifndef _XBOX
#include <winsock2.h>
#include <mmsystem.h>
#endif
#endif
#if defined(__PSL1GHT__)
#include <sys/time.h>
#elif defined(__CELLOS_LV2__)
#include <sys/sys_time.h>
#endif
#ifdef GEKKO
#include <ogc/lwp_watchdog.h>
#endif
#ifdef __linux__
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#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)

View File

@ -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 <unistd.h>
#endif
#if defined(__CELLOS_LV2__) || defined(GEKKO)
#ifndef _PPU_INTRINSICS_H
#include <ppu_intrinsics.h>
#endif
#elif defined(_WIN32) && !defined(_XBOX)
#include <mmsystem.h>
#elif defined(_XBOX360)
#include <PPCIntrinsics.h>
#elif defined(__linux__)
#include <sys/time.h>
#elif defined(_POSIX_MONOTONIC_CLOCK) || defined(ANDROID)
// POSIX_MONOTONIC_CLOCK is not being defined in Android headers despite support being present.
#include <time.h>
#endif
#if defined(__PSL1GHT__)
#include <sys/time.h>
#elif defined(__CELLOS_LV2__)
#include <sys/sys_time.h>
#endif
#ifdef GEKKO
#include <ogc/lwp_watchdog.h>
#endif
// OSX specific. OSX lacks clock_gettime().
#ifdef __MACH__
#include <mach/clock.h>
#include <mach/mach.h>
#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

View File

@ -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 <stdint.h>
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);