mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-25 17:10:12 +00:00
230 lines
6.7 KiB
C
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;
|
|
}
|