1 | /**************************************************************************/ |
2 | /* main_timer_sync.h */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #ifndef MAIN_TIMER_SYNC_H |
32 | #define MAIN_TIMER_SYNC_H |
33 | |
34 | #include "core/config/engine.h" |
35 | |
36 | // Uncomment this define to get more debugging logs for the delta smoothing. |
37 | // #define GODOT_DEBUG_DELTA_SMOOTHER |
38 | |
39 | struct MainFrameTime { |
40 | double process_step; // delta time to advance during process() |
41 | int physics_steps; // number of times to iterate the physics engine |
42 | double interpolation_fraction; // fraction through the current physics tick |
43 | |
44 | void clamp_process_step(double min_process_step, double max_process_step); |
45 | }; |
46 | |
47 | class MainTimerSync { |
48 | class DeltaSmoother { |
49 | public: |
50 | // pass the recorded delta, returns a smoothed delta |
51 | int64_t smooth_delta(int64_t p_delta); |
52 | |
53 | private: |
54 | void update_refresh_rate_estimator(int64_t p_delta); |
55 | bool fps_allows_smoothing(int64_t p_delta); |
56 | |
57 | // estimated vsync delta (monitor refresh rate) |
58 | int64_t _vsync_delta = 16666; |
59 | |
60 | // keep track of accumulated time so we know how many vsyncs to advance by |
61 | int64_t _leftover_time = 0; |
62 | |
63 | // keep a rough measurement of the FPS as we run. |
64 | // If this drifts a long way below or above the refresh rate, the machine |
65 | // is struggling to keep up, and we can switch off smoothing. This |
66 | // also deals with the case that the user has overridden the vsync in the GPU settings, |
67 | // in which case we don't want to try smoothing. |
68 | static const int MEASURE_FPS_OVER_NUM_FRAMES = 64; |
69 | |
70 | int64_t _measurement_time = 0; |
71 | int64_t _measurement_frame_count = 0; |
72 | int64_t _measurement_end_frame = MEASURE_FPS_OVER_NUM_FRAMES; |
73 | int64_t _measurement_start_time = 0; |
74 | bool _measurement_allows_smoothing = true; |
75 | |
76 | // we can estimate the fps by growing it on condition |
77 | // that a large proportion of frames are higher than the current estimate. |
78 | int32_t _estimated_fps = 0; |
79 | int32_t _hits_at_estimated = 0; |
80 | int32_t _hits_above_estimated = 0; |
81 | int32_t _hits_below_estimated = 0; |
82 | int32_t _hits_one_above_estimated = 0; |
83 | int32_t _hits_one_below_estimated = 0; |
84 | bool _estimate_complete = false; |
85 | bool _estimate_locked = false; |
86 | |
87 | // data for averaging the delta over a second or so |
88 | // to prevent spurious values |
89 | int64_t _estimator_total_delta = 0; |
90 | int32_t _estimator_delta_readings = 0; |
91 | |
92 | void made_new_estimate() { |
93 | _hits_above_estimated = 0; |
94 | _hits_at_estimated = 0; |
95 | _hits_below_estimated = 0; |
96 | _hits_one_above_estimated = 0; |
97 | _hits_one_below_estimated = 0; |
98 | |
99 | _estimate_complete = false; |
100 | |
101 | #ifdef GODOT_DEBUG_DELTA_SMOOTHER |
102 | print_line("estimated fps " + itos(_estimated_fps)); |
103 | #endif |
104 | } |
105 | |
106 | } _delta_smoother; |
107 | |
108 | // wall clock time measured on the main thread |
109 | uint64_t last_cpu_ticks_usec = 0; |
110 | uint64_t current_cpu_ticks_usec = 0; |
111 | |
112 | // logical game time since last physics timestep |
113 | double time_accum = 0; |
114 | |
115 | // current difference between wall clock time and reported sum of process_steps |
116 | double time_deficit = 0; |
117 | |
118 | // number of frames back for keeping accumulated physics steps roughly constant. |
119 | // value of 12 chosen because that is what is required to make 144 Hz monitors |
120 | // behave well with 60 Hz physics updates. The only worse commonly available refresh |
121 | // would be 85, requiring CONTROL_STEPS = 17. |
122 | static const int CONTROL_STEPS = 12; |
123 | |
124 | // sum of physics steps done over the last (i+1) frames |
125 | int accumulated_physics_steps[CONTROL_STEPS]; |
126 | |
127 | // typical value for accumulated_physics_steps[i] is either this or this plus one |
128 | int typical_physics_steps[CONTROL_STEPS]; |
129 | |
130 | int fixed_fps = 0; |
131 | |
132 | protected: |
133 | // returns the fraction of p_physics_step required for the timer to overshoot |
134 | // before advance_core considers changing the physics_steps return from |
135 | // the typical values as defined by typical_physics_steps |
136 | double get_physics_jitter_fix(); |
137 | |
138 | // gets our best bet for the average number of physics steps per render frame |
139 | // return value: number of frames back this data is consistent |
140 | int get_average_physics_steps(double &p_min, double &p_max); |
141 | |
142 | // advance physics clock by p_process_step, return appropriate number of steps to simulate |
143 | MainFrameTime advance_core(double p_physics_step, int p_physics_ticks_per_second, double p_process_step); |
144 | |
145 | // calls advance_core, keeps track of deficit it adds to animaption_step, make sure the deficit sum stays close to zero |
146 | MainFrameTime advance_checked(double p_physics_step, int p_physics_ticks_per_second, double p_process_step); |
147 | |
148 | // determine wall clock step since last iteration |
149 | double get_cpu_process_step(); |
150 | |
151 | public: |
152 | MainTimerSync(); |
153 | |
154 | // start the clock |
155 | void init(uint64_t p_cpu_ticks_usec); |
156 | // set measured wall clock time |
157 | void set_cpu_ticks_usec(uint64_t p_cpu_ticks_usec); |
158 | //set fixed fps |
159 | void set_fixed_fps(int p_fixed_fps); |
160 | |
161 | // advance one frame, return timesteps to take |
162 | MainFrameTime advance(double p_physics_step, int p_physics_ticks_per_second); |
163 | }; |
164 | |
165 | #endif // MAIN_TIMER_SYNC_H |
166 | |