|
4 | 4 | #define NOMINMAX |
5 | 5 | #include <Windows.h> |
6 | 6 | #elif defined(__linux__) |
7 | | -#include "time.h" |
| 7 | +#include <time.h> |
| 8 | +#include <errno.h> |
8 | 9 | #endif |
9 | 10 | #include <stdio.h> |
10 | 11 | #include <stdlib.h> |
@@ -52,6 +53,10 @@ struct te_renderer { |
52 | 53 |
|
53 | 54 | unsigned int fps_limit; |
54 | 55 |
|
| 56 | +#if defined(__linux__) |
| 57 | + struct timespec frame_end_time; |
| 58 | +#endif |
| 59 | + |
55 | 60 | #if defined(ENGINE_DEBUG_TOOLS) |
56 | 61 | // GPU time query IDs. |
57 | 62 | unsigned int gl_timestamp_frame_start; |
@@ -107,6 +112,10 @@ renderer_create(struct te_window* window) { |
107 | 112 |
|
108 | 113 | renderer->fps_limit = 0; |
109 | 114 |
|
| 115 | +#if defined(__linux__) |
| 116 | + clock_gettime(CLOCK_MONOTONIC, &renderer->frame_end_time); |
| 117 | +#endif |
| 118 | + |
110 | 119 | // Create GL context. |
111 | 120 | renderer->gl_context = SDL_GL_CreateContext(prv_window_get_sdl_window(window)); |
112 | 121 | if (renderer->gl_context == NULL) { |
@@ -267,24 +276,43 @@ prv_renderer_calc_frame_stats(te_renderer* renderer, float delta_time_sec) { |
267 | 276 |
|
268 | 277 | // FPS limit. |
269 | 278 | if (renderer->fps_limit > 0) { |
270 | | - // Should use perf counters and high precision timers here but this already works fine. |
| 279 | +#if defined(WIN32) |
271 | 280 | const float target_time_ms = 1000.0f / (float)renderer->fps_limit; |
272 | 281 | const float time_to_sleep_ms = target_time_ms - delta_time_sec; |
273 | 282 | if (time_to_sleep_ms >= 1.0) { |
274 | | -#if defined(WIN32) |
275 | 283 | timeBeginPeriod(1); |
276 | 284 | Sleep((unsigned long)time_to_sleep_ms); |
277 | 285 | timeEndPeriod(1); |
| 286 | + } |
278 | 287 | #elif defined(__linux__) |
279 | | - struct timespec tim; |
280 | | - struct timespec tim2; |
281 | | - tim.tv_sec = 0; |
282 | | - tim.tv_nsec = (long)floor(time_to_sleep_ms * 1000000.0f * 0.965f); |
283 | | - nanosleep(&tim, &tim2); |
| 288 | + const long target_time_ns = (long)(1000000000.0 / (double)renderer->fps_limit); |
| 289 | + |
| 290 | + struct timespec now; |
| 291 | + clock_gettime(CLOCK_MONOTONIC, &now); |
| 292 | + long delta_ns = now.tv_nsec - renderer->frame_end_time.tv_nsec; |
| 293 | + if (delta_ns < 0) { |
| 294 | + delta_ns += 1000000000; |
| 295 | + } |
| 296 | + |
| 297 | + const long time_to_sleep_ns = target_time_ns - delta_ns; |
| 298 | + if (time_to_sleep_ns > 0) { |
| 299 | + struct timespec requested; |
| 300 | + struct timespec remaining; |
| 301 | + requested.tv_sec = 0; |
| 302 | + requested.tv_nsec = time_to_sleep_ns; |
| 303 | + while (nanosleep(&requested, &remaining) == -1) { |
| 304 | + if (errno == EINTR) { |
| 305 | + requested = remaining; |
| 306 | + } else { |
| 307 | + break; |
| 308 | + } |
| 309 | + } |
| 310 | + } |
| 311 | + |
| 312 | + clock_gettime(CLOCK_MONOTONIC, &renderer->frame_end_time); |
284 | 313 | #else |
285 | 314 | #error "unsupported OS" |
286 | 315 | #endif |
287 | | - } |
288 | 316 | } |
289 | 317 | } |
290 | 318 |
|
|
0 commit comments