Skip to main content

FramePacer.ixx File

Module defining the FramePacer class for frame rate control and timing synchronization. More...

Included Headers

#include <memory> #include <thread> #include <chrono> #include <cassert> #include <helios.engine.tooling.FrameStats> #include <helios.util.time.Stopwatch>

Namespaces Index

namespacehelios
namespaceengine

Main engine module aggregating core infrastructure and game systems. More...

namespacetooling

Tooling utilities for diagnostics, metrics and developer overlays. More...

Classes Index

classFramePacer

Controls and maintains a target frame rate through precise timing and pacing. More...

Description

Module defining the FramePacer class for frame rate control and timing synchronization.

File Listing

The file content with the documentation metadata removed is:

1/**
2 * @file FramePacer.ixx
3 * @brief Module defining the FramePacer class for frame rate control and timing synchronization.
4 */
5module;
6
7#include <memory>
8#include <thread>
9#include <chrono>
10#include <cassert>
11
12export module helios.engine.tooling.FramePacer;
13
14import helios.util.time.Stopwatch;
15import helios.engine.tooling.FrameStats;
16
17export namespace helios::engine::tooling {
18
19 /**
20 * @class FramePacer
21 * @brief Controls and maintains a target frame rate through precise timing and pacing.
22 *
23 * The FramePacer class utilizes a `Stopwatch` to measure frame execution time and
24 * introduces necessary sleep delays to maintain a consistent target frame rate.
25 * It returns detailed timing statistics via the `FrameStats` structure upon synchronization.
26 *
27 * @details The pacing mechanism helps in achieving smoother frame delivery by minimizing
28 * jitter, although strict adherence depends on the OS scheduler's precision.
29 *
30 * @note If `targetFps` is set to 0.0f, no frame pacing is performed, and frames
31 * run as fast as the hardware and OS allow (unlocked framerate).
32 *
33 * @par Usage Example:
34 * ```cpp
35 * auto stopwatch = std::make_unique<helios::util::time::Stopwatch>();
36 * FramePacer pacer(std::move(stopwatch));
37 * pacer.setTargetFps(60.0f);
38 *
39 * while (running) {
40 * pacer.beginFrame();
41 * // ... game logic and rendering ...
42 * FrameStats stats = pacer.sync();
43 * }
44 * ```
45 */
46 class FramePacer {
47 /**
48 * @brief The stopwatch used for high-resolution time measurement.
49 */
50 std::unique_ptr<helios::util::time::Stopwatch> stopwatch_;
51
52 /**
53 * @brief The target frame rate in Frames Per Second (FPS).
54 */
55 float targetFps_ = 0.0f;
56
57 public:
58 /**
59 * @brief Constructs a FramePacer with the given stopwatch.
60 *
61 * Initializes a new FramePacer instance in unlimited FPS mode (targetFps = 0.0f).
62 *
63 * @param stopwatch Unique pointer to a valid `Stopwatch` instance. Ownership
64 * is transferred to the FramePacer.
65 */
66 explicit FramePacer(std::unique_ptr<helios::util::time::Stopwatch> stopwatch) :
67 stopwatch_(std::move(stopwatch)) {
68 assert(stopwatch_ && "FramePacer requires a valid Stopwatch (non-null)");
69 }
70
71 /**
72 * @brief Sets the desired target frame rate.
73 *
74 * @param fps The target frame rate in Frames Per Second (FPS).
75 * Set to 0.0f to disable pacing (unlocked framerate).
76 */
77 void setTargetFps(float fps) {
78 targetFps_ = fps;
79 }
80
81 /**
82 * @brief Retrieves the current target frame rate.
83 *
84 * Returns the frame rate target that has been set for pacing, expressed in
85 * Frames Per Second (FPS). If the target frame rate is set to 0.0f, frame pacing
86 * is disabled, and the frame rate is unlocked.
87 *
88 * @return The target frame rate in FPS.
89 *
90 * @note A return value of 0.0f indicates that the frame pacing mechanism is not active.
91 */
92 [[nodiscard]] float getTargetFps() const noexcept {
93 return targetFps_;
94 }
95
96 /**
97 * @brief Marks the beginning of a new frame.
98 *
99 * Starts the internal stopwatch to begin measuring the frame's work time.
100 * This method must be called at the very beginning of each frame cycle,
101 * before any game logic, physics, or rendering operations.
102 */
103 void beginFrame() {
104 stopwatch_->start();
105 }
106
107 /**
108 * @brief Synchronizes frame timing and returns frame statistics.
109 *
110 * Measures the elapsed work time since `beginFrame()`. If a target FPS is set
111 * and the work time is less than the target frame duration, this method
112 * sleeps the current thread to meet the target timing.
113 *
114 * @return A `FrameStats` structure containing the total frame time (including wait),
115 * the actual work time (CPU processing), and the wait time (idle).
116 *
117 * @note If `targetFps` is 0.0f or the frame took longer than the target duration,
118 * no sleeping occurs, and `waitTime` in the returned stats will be 0.0f.
119 *
120 * @todo Implement hybrid spinning for the last millisecond of the wait time
121 * to improve timing precision and mitigate OS scheduler wake-up latency.
122 */
123 [[nodiscard]] FrameStats sync() {
124 float workTime = stopwatch_->elapsedSeconds();
125
126 float waitTime = 0.0f;
127 float totalTime = workTime;
128
129 if (targetFps_ > 0.0f) {
130 float targetTime = 1.0f / targetFps_;
131 if (targetTime > workTime) {
132 auto requestedWaitTime = targetTime - workTime;
133 auto sleepDuration = std::chrono::duration<float>(requestedWaitTime);
134 std::this_thread::sleep_for(sleepDuration);
135 totalTime = stopwatch_->elapsedSeconds();
136 waitTime = totalTime - workTime;
137 }
138 }
139
140 return FrameStats{ totalTime, workTime, waitTime };
141 }
142 };
143}

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.15.0.