Files
test/source/blender/editors/scene/scene_fps.cc
Campbell Barton eec449ffe8 Cleanup: correct spelling, comments
Hyphenate words in GLSL code-comments.
2023-08-29 15:55:09 +10:00

204 lines
6.1 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edscene
*
* Define `ED_scene_fps_*` functions.
*/
#include <cmath>
#include "BLI_math_base.h"
#include "DNA_scene_types.h"
#include "ED_scene.hh"
#include "MEM_guardedalloc.h"
using FrameSampleT = uint32_t;
using FrameSumT = uint64_t;
/**
* This values gives enough precision while not overflowing a 64-bit integer when accumulating.
* Compared to the same calculation with `double` the error rate is less than 0.5 microsecond,
* more than enough precision for FPS display.
*/
#define FIXED_UNIT FrameSampleT(65535)
/** Use report the difference (error) between fixed-point arithmetic and double-precision. */
// #define USE_DEBUG_REPORT_ERROR_MARGIN
struct FrameSample {
FrameSampleT value;
#ifdef USE_DEBUG_REPORT_ERROR_MARGIN
double value_db;
#endif
};
/**
* For playback frame-rate info stored during runtime as `scene->fps_info`.
*/
struct ScreenFrameRateInfo {
double time_curr;
double time_prev;
#ifdef USE_DEBUG_REPORT_ERROR_MARGIN
double error_sum;
int error_samples;
#endif
/** When the target FPS is not a whole number. */
bool fps_target_is_fractional;
/** The target FPS, use to reset on change. */
float fps_target;
/** Final result, ignore when -1.0. */
float fps_average;
int times_fps_index;
int times_fps_num;
int times_fps_num_set;
FrameSumT times_fps_sum;
/** Over allocate (containing `times_fps_num` elements). */
FrameSample times_fps[0];
};
void ED_scene_fps_average_clear(Scene *scene)
{
if (scene->fps_info == nullptr) {
return;
}
#ifndef NDEBUG
/* Assert this value has not somehow become out of sync as this would mean
* the reported frame-rate would be wrong. */
ScreenFrameRateInfo *fpsi = static_cast<ScreenFrameRateInfo *>(scene->fps_info);
FrameSumT times_fps_sum_cmp = 0;
for (int i = 0; i < fpsi->times_fps_num_set; i++) {
times_fps_sum_cmp += fpsi->times_fps[i].value;
}
BLI_assert(fpsi->times_fps_sum == times_fps_sum_cmp);
#endif
MEM_freeN(scene->fps_info);
scene->fps_info = nullptr;
}
void ED_scene_fps_average_accumulate(Scene *scene, const short fps_samples, const double ltime)
{
const float fps_target = float(FPS);
const int times_fps_num = (fps_samples > 0) ? fps_samples : max_ii(1, int(ceilf(fps_target)));
ScreenFrameRateInfo *fpsi = static_cast<ScreenFrameRateInfo *>(scene->fps_info);
if (fpsi) {
/* Reset when the target FPS changes.
* Needed redraw times from when a different FPS was set do not contribute
* to an average that is over/under the new target. */
if ((fpsi->fps_target != fps_target) || (fpsi->times_fps_num != times_fps_num)) {
MEM_freeN(fpsi);
fpsi = nullptr;
scene->fps_info = nullptr;
}
}
/* If there isn't any info, initialize it first. */
if (fpsi == nullptr) {
scene->fps_info = MEM_callocN(
sizeof(ScreenFrameRateInfo) + (sizeof(FrameSample) * times_fps_num), __func__);
fpsi = static_cast<ScreenFrameRateInfo *>(scene->fps_info);
fpsi->fps_target = fps_target;
fpsi->times_fps_num = times_fps_num;
fpsi->times_fps_num_set = 0;
/* Use 100 for 2 decimal places (currently used for FPS display), could be configurable. */
const double decimal_places = 100.0;
fpsi->fps_target_is_fractional = std::round(fps_target) !=
(std::round(fps_target * decimal_places) / decimal_places);
}
/* Update the values. */
fpsi->time_curr = fpsi->time_prev;
fpsi->time_prev = ltime;
/* Mark as outdated. */
fpsi->fps_average = -1.0f;
}
bool ED_scene_fps_average_calc(const Scene *scene, SceneFPS_State *r_state)
{
ScreenFrameRateInfo *fpsi = static_cast<ScreenFrameRateInfo *>(scene->fps_info);
if (fpsi == nullptr) {
return false;
}
if (fpsi->time_prev == 0.0 || fpsi->time_curr == 0.0) {
/* The user should never see this. */
fpsi->fps_average = -1.0f;
return false;
}
if (fpsi->fps_average == -1.0) {
/* Doing an average for a more robust calculation. */
if (fpsi->times_fps_index >= fpsi->times_fps_num) {
fpsi->times_fps_index = 0;
}
const double fps_sample = 1.0 / (fpsi->time_prev - fpsi->time_curr);
{
const FrameSampleT fps_prev = (fpsi->times_fps_num_set == fpsi->times_fps_num) ?
fpsi->times_fps[fpsi->times_fps_index].value :
FrameSampleT(0);
const double fps_curr_db = std::round(fps_sample * double(FIXED_UNIT));
BLI_assert(fps_curr_db >= 0.0f);
const FrameSampleT fps_curr = FrameSampleT(fps_curr_db);
fpsi->times_fps[fpsi->times_fps_index].value = fps_curr;
fpsi->times_fps_sum -= fps_prev;
fpsi->times_fps_sum += fps_curr;
}
#ifdef USE_DEBUG_REPORT_ERROR_MARGIN
fpsi->times_fps[fpsi->times_fps_index].value_db = fps_sample;
#endif
fpsi->times_fps_index++;
if (fpsi->times_fps_index > fpsi->times_fps_num_set) {
fpsi->times_fps_num_set = fpsi->times_fps_index;
}
BLI_assert(fpsi->times_fps_num_set > 0);
fpsi->fps_average = float((double(fpsi->times_fps_sum) / double(fpsi->times_fps_num_set)) /
FIXED_UNIT);
#ifdef USE_DEBUG_REPORT_ERROR_MARGIN
{
double fps_average_ref = 0.0f;
for (int i = 0; i < fpsi->times_fps_num_set; i++) {
fps_average_ref += fpsi->times_fps[i].value_db;
}
fps_average_ref = float(fps_average_ref / double(fpsi->times_fps_num_set));
const float error = float(fps_average_ref) - fpsi->fps_average;
fpsi->error_sum += error;
fpsi->error_samples += 1;
if ((fpsi->error_samples % 100) == 0) {
printf("%s error: %.16f over %d samples (average %.16f)\n",
__func__,
fpsi->error_sum,
fpsi->error_samples,
fpsi->error_sum / double(fpsi->error_samples));
}
}
#endif /* USE_DEBUG_REPORT_ERROR_MARGIN */
}
r_state->fps_average = fpsi->fps_average;
r_state->fps_target = fpsi->fps_target;
r_state->fps_target_is_fractional = fpsi->fps_target_is_fractional;
return true;
}