RetroArch/gfx/video_monitor.c
2015-03-21 04:43:18 +01:00

230 lines
6.7 KiB
C

/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
* Copyright (C) 2011-2015 - Daniel De Matteis
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "video_monitor.h"
#include "../general.h"
#include "../retroarch.h"
#include "../runloop.h"
#include "../performance.h"
void video_monitor_adjust_system_rates(void)
{
float timing_skew;
global_t *global = global_get_ptr();
const struct retro_system_timing *info =
(const struct retro_system_timing*)&global->system.av_info.timing;
settings_t *settings = config_get_ptr();
global->system.force_nonblock = false;
if (info->fps <= 0.0)
return;
timing_skew = fabs(1.0f - info->fps / settings->video.refresh_rate);
/* We don't want to adjust pitch too much. If we have extreme cases,
* just don't readjust at all. */
if (timing_skew <= settings->audio.max_timing_skew)
return;
RARCH_LOG("Timings deviate too much. Will not adjust. (Display = %.2f Hz, Game = %.2f Hz)\n",
settings->video.refresh_rate,
(float)info->fps);
if (info->fps <= settings->video.refresh_rate)
return;
/* We won't be able to do VSync reliably when game FPS > monitor FPS. */
global->system.force_nonblock = true;
RARCH_LOG("Game FPS > Monitor FPS. Cannot rely on VSync.\n");
}
/**
* video_monitor_set_refresh_rate:
* @hz : New refresh rate for monitor.
*
* Sets monitor refresh rate to new value.
**/
void video_monitor_set_refresh_rate(float hz)
{
char msg[PATH_MAX_LENGTH];
settings_t *settings = config_get_ptr();
snprintf(msg, sizeof(msg), "Setting refresh rate to: %.3f Hz.", hz);
rarch_main_msg_queue_push(msg, 1, 180, false);
RARCH_LOG("%s\n", msg);
settings->video.refresh_rate = hz;
}
/**
* video_monitor_compute_fps_statistics:
*
* Computes monitor FPS statistics.
**/
void video_monitor_compute_fps_statistics(void)
{
double avg_fps = 0.0, stddev = 0.0;
unsigned samples = 0;
runloop_t *runloop = rarch_main_get_ptr();
settings_t *settings = config_get_ptr();
if (settings->video.threaded)
{
RARCH_LOG("Monitor FPS estimation is disabled for threaded video.\n");
return;
}
if (runloop->measure_data.frame_time_samples_count <
2 * MEASURE_FRAME_TIME_SAMPLES_COUNT)
{
RARCH_LOG(
"Does not have enough samples for monitor refresh rate estimation. Requires to run for at least %u frames.\n",
2 * MEASURE_FRAME_TIME_SAMPLES_COUNT);
return;
}
if (video_monitor_fps_statistics(&avg_fps, &stddev, &samples))
{
RARCH_LOG("Average monitor Hz: %.6f Hz. (%.3f %% frame time deviation, based on %u last samples).\n",
avg_fps, 100.0 * stddev, samples);
}
}
/**
* video_monitor_fps_statistics
* @refresh_rate : Monitor refresh rate.
* @deviation : Deviation from measured refresh rate.
* @sample_points : Amount of sampled points.
*
* Gets the monitor FPS statistics based on the current
* runtime.
*
* Returns: true (1) on success.
* false (0) if:
* a) threaded video mode is enabled
* b) less than 2 frame time samples.
* c) FPS monitor enable is off.
**/
bool video_monitor_fps_statistics(double *refresh_rate,
double *deviation, unsigned *sample_points)
{
unsigned i;
retro_time_t accum = 0, avg, accum_var = 0;
unsigned samples = 0;
runloop_t *runloop = rarch_main_get_ptr();
settings_t *settings = config_get_ptr();
samples = min(MEASURE_FRAME_TIME_SAMPLES_COUNT,
runloop->measure_data.frame_time_samples_count);
if (settings->video.threaded || (samples < 2))
return false;
/* Measure statistics on frame time (microsecs), *not* FPS. */
for (i = 0; i < samples; i++)
accum += runloop->measure_data.frame_time_samples[i];
#if 0
for (i = 0; i < samples; i++)
RARCH_LOG("Interval #%u: %d usec / frame.\n",
i, (int)runloop->measure_data.frame_time_samples[i]);
#endif
avg = accum / samples;
/* Drop first measurement. It is likely to be bad. */
for (i = 0; i < samples; i++)
{
retro_time_t diff = runloop->measure_data.frame_time_samples[i] - avg;
accum_var += diff * diff;
}
*deviation = sqrt((double)accum_var / (samples - 1)) / avg;
*refresh_rate = 1000000.0 / avg;
*sample_points = samples;
return true;
}
#ifndef TIME_TO_FPS
#define TIME_TO_FPS(last_time, new_time, frames) ((1000000.0f * (frames)) / ((new_time) - (last_time)))
#endif
#define FPS_UPDATE_INTERVAL 256
/**
* video_monitor_get_fps:
* @buf : string suitable for Window title
* @size : size of buffer.
* @buf_fps : string of raw FPS only (optional).
* @size_fps : size of raw FPS buffer.
*
* Get the amount of frames per seconds.
*
* Returns: true if framerate per seconds could be obtained,
* otherwise false.
*
**/
bool video_monitor_get_fps(char *buf, size_t size,
char *buf_fps, size_t size_fps)
{
static float last_fps;
retro_time_t new_time;
static retro_time_t curr_time;
static retro_time_t fps_time;
runloop_t *runloop = rarch_main_get_ptr();
global_t *global = global_get_ptr();
*buf = '\0';
new_time = rarch_get_time_usec();
if (runloop->frames.video.count)
{
bool ret = false;
unsigned write_index =
runloop->measure_data.frame_time_samples_count++ &
(MEASURE_FRAME_TIME_SAMPLES_COUNT - 1);
runloop->measure_data.frame_time_samples[write_index] =
new_time - fps_time;
fps_time = new_time;
if ((runloop->frames.video.count % FPS_UPDATE_INTERVAL) == 0)
{
last_fps = TIME_TO_FPS(curr_time, new_time, FPS_UPDATE_INTERVAL);
curr_time = new_time;
snprintf(buf, size, "%s || FPS: %6.1f || Frames: %u",
global->title_buf, last_fps, runloop->frames.video.count);
ret = true;
}
if (buf_fps)
snprintf(buf_fps, size_fps, "FPS: %6.1f || Frames: %u",
last_fps, runloop->frames.video.count);
return ret;
}
curr_time = fps_time = new_time;
strlcpy(buf, global->title_buf, size);
if (buf_fps)
strlcpy(buf_fps, "N/A", size_fps);
return true;
}