libtcod
Loading...
Searching...
No Matches
timer.hpp
Go to the documentation of this file.
1/* BSD 3-Clause License
2 *
3 * Copyright © 2008-2026, Jice and the libtcod contributors.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * 3. Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
34#pragma once
35#ifndef LIBTCOD_TIMER_HPP_
36#define LIBTCOD_TIMER_HPP_
37#ifndef NO_SDL
38#include <SDL3/SDL_timer.h>
39
40#include <algorithm>
41#include <cstddef>
42#include <cstdint>
43#include <deque>
44#include <numeric>
45#include <stdexcept>
46#include <vector>
47
48#include "config.h"
49
50namespace tcod {
51
68class [[nodiscard]] Timer {
69 public:
70
73 Timer() : last_time_{SDL_GetPerformanceCounter()} {}
74
84 float sync(int desired_fps = 0) {
85 const uint64_t frequency = SDL_GetPerformanceFrequency();
86 uint64_t current_time = SDL_GetPerformanceCounter(); // The precise current time.
87 int64_t delta_time = static_cast<int64_t>(current_time - last_time_); // The precise delta time.
88 if (desired_fps > 0) {
89 const int64_t desired_delta_time = frequency / desired_fps; // Desired precise delta time.
90 const int64_t time_until_next_frame_ms =
91 (desired_delta_time - delta_time) * 1000 / static_cast<int64_t>(frequency);
92 if (time_until_next_frame_ms > 2) {
93 // Sleep until 1 millisecond before the target time.
94 SDL_Delay(static_cast<uint32_t>(time_until_next_frame_ms) - 1);
95 }
96 while (delta_time < desired_delta_time) { // Spin for the remaining time.
97 current_time = SDL_GetPerformanceCounter();
98 delta_time = static_cast<int64_t>(current_time - last_time_);
99 }
100 }
101 last_time_ = current_time;
102 const float delta_time_s = std::max(0.0f, static_cast<float>(delta_time) / frequency); // Delta time in seconds.
103 // Drop samples as they hit the total time and count limits.
104 double total_time = std::accumulate(samples_.begin(), samples_.end(), 0.0); // Total time of all samples.
105 while (!samples_.empty() && (total_time > MAX_SAMPLES_TIME || samples_.size() >= MAX_SAMPLES_COUNT)) {
106 total_time -= samples_.front();
107 samples_.pop_front();
108 }
109 samples_.push_back(delta_time_s);
110 return delta_time_s;
111 }
112
115 [[nodiscard]] float get_mean_fps() const noexcept {
116 if (samples_.empty()) return 0;
117 const double total_time = std::accumulate(samples_.begin(), samples_.end(), 0.0);
118 if (total_time == 0) return 0;
119 return 1.0f / static_cast<float>(total_time / static_cast<double>(samples_.size()));
120 }
121
124 [[nodiscard]] float get_last_fps() const noexcept {
125 if (samples_.empty()) return 0;
126 if (samples_.back() == 0) return 0;
127 return 1.0f / samples_.back();
128 }
129
132 [[nodiscard]] float get_min_fps() const noexcept {
133 if (samples_.empty()) return 0;
134 const float sample = *std::max_element(samples_.begin(), samples_.end());
135 if (sample == 0) return 0;
136 return 1.0f / sample;
137 }
138
141 [[nodiscard]] float get_max_fps() const noexcept {
142 if (samples_.empty()) return 0;
143 const float sample = *std::min_element(samples_.begin(), samples_.end());
144 if (sample == 0) return 0;
145 return 1.0f / sample;
146 }
147
150 [[nodiscard]] float get_median_fps() const noexcept {
151 if (samples_.empty()) return 0;
152 std::vector<float> samples(samples_.begin(), samples_.end());
153 std::sort(samples.begin(), samples.end());
154 float median_sample = samples[samples.size() / 2];
155 if (samples.size() % 2 == 0 && samples.size() > 2) {
156 median_sample = (median_sample + samples[samples.size() / 2 + 1]) / 2.0f;
157 }
158 if (median_sample == 0) return 0;
159 return 1.0f / median_sample;
160 }
161
162 private:
163 static constexpr size_t MAX_SAMPLES_COUNT = 1024; // Hard limit on the number of samples held.
164 static constexpr double MAX_SAMPLES_TIME = 1.0; // Hard limit on the total time of samples held.
165
166 uint64_t last_time_; // The last precise time sampled.
167 std::deque<float> samples_; // The most recent delta time samples in seconds.
168};
169} // namespace tcod
170#endif // NO_SDL
171#endif // LIBTCOD_TIMER_HPP_
Libtcod config header.
The libtcod namespace.
Definition color.hpp:45