summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/timer
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/timer
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/timer')
-rw-r--r--contrib/SDL-3.2.8/src/timer/SDL_timer.c734
-rw-r--r--contrib/SDL-3.2.8/src/timer/SDL_timer_c.h39
-rw-r--r--contrib/SDL-3.2.8/src/timer/haiku/SDL_systimer.c43
-rw-r--r--contrib/SDL-3.2.8/src/timer/n3ds/SDL_systimer.c43
-rw-r--r--contrib/SDL-3.2.8/src/timer/ps2/SDL_systimer.c50
-rw-r--r--contrib/SDL-3.2.8/src/timer/psp/SDL_systimer.c54
-rw-r--r--contrib/SDL-3.2.8/src/timer/unix/SDL_systimer.c188
-rw-r--r--contrib/SDL-3.2.8/src/timer/vita/SDL_systimer.c51
-rw-r--r--contrib/SDL-3.2.8/src/timer/windows/SDL_systimer.c133
9 files changed, 1335 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/timer/SDL_timer.c b/contrib/SDL-3.2.8/src/timer/SDL_timer.c
new file mode 100644
index 0000000..2fa6553
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/timer/SDL_timer.c
@@ -0,0 +1,734 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#include "SDL_timer_c.h"
24#include "../thread/SDL_systhread.h"
25
26// #define DEBUG_TIMERS
27
28#if !defined(SDL_PLATFORM_EMSCRIPTEN) || !defined(SDL_THREADS_DISABLED)
29
30typedef struct SDL_Timer
31{
32 SDL_TimerID timerID;
33 SDL_TimerCallback callback_ms;
34 SDL_NSTimerCallback callback_ns;
35 void *userdata;
36 Uint64 interval;
37 Uint64 scheduled;
38 SDL_AtomicInt canceled;
39 struct SDL_Timer *next;
40} SDL_Timer;
41
42typedef struct SDL_TimerMap
43{
44 SDL_TimerID timerID;
45 SDL_Timer *timer;
46 struct SDL_TimerMap *next;
47} SDL_TimerMap;
48
49// The timers are kept in a sorted list
50typedef struct
51{
52 // Data used by the main thread
53 SDL_InitState init;
54 SDL_Thread *thread;
55 SDL_TimerMap *timermap;
56 SDL_Mutex *timermap_lock;
57
58 // Padding to separate cache lines between threads
59 char cache_pad[SDL_CACHELINE_SIZE];
60
61 // Data used to communicate with the timer thread
62 SDL_SpinLock lock;
63 SDL_Semaphore *sem;
64 SDL_Timer *pending;
65 SDL_Timer *freelist;
66 SDL_AtomicInt active;
67
68 // List of timers - this is only touched by the timer thread
69 SDL_Timer *timers;
70} SDL_TimerData;
71
72static SDL_TimerData SDL_timer_data;
73
74/* The idea here is that any thread might add a timer, but a single
75 * thread manages the active timer queue, sorted by scheduling time.
76 *
77 * Timers are removed by simply setting a canceled flag
78 */
79
80static void SDL_AddTimerInternal(SDL_TimerData *data, SDL_Timer *timer)
81{
82 SDL_Timer *prev, *curr;
83
84 prev = NULL;
85 for (curr = data->timers; curr; prev = curr, curr = curr->next) {
86 if (curr->scheduled > timer->scheduled) {
87 break;
88 }
89 }
90
91 // Insert the timer here!
92 if (prev) {
93 prev->next = timer;
94 } else {
95 data->timers = timer;
96 }
97 timer->next = curr;
98}
99
100static int SDLCALL SDL_TimerThread(void *_data)
101{
102 SDL_TimerData *data = (SDL_TimerData *)_data;
103 SDL_Timer *pending;
104 SDL_Timer *current;
105 SDL_Timer *freelist_head = NULL;
106 SDL_Timer *freelist_tail = NULL;
107 Uint64 tick, now, interval, delay;
108
109 /* Threaded timer loop:
110 * 1. Queue timers added by other threads
111 * 2. Handle any timers that should dispatch this cycle
112 * 3. Wait until next dispatch time or new timer arrives
113 */
114 for (;;) {
115 // Pending and freelist maintenance
116 SDL_LockSpinlock(&data->lock);
117 {
118 // Get any timers ready to be queued
119 pending = data->pending;
120 data->pending = NULL;
121
122 // Make any unused timer structures available
123 if (freelist_head) {
124 freelist_tail->next = data->freelist;
125 data->freelist = freelist_head;
126 }
127 }
128 SDL_UnlockSpinlock(&data->lock);
129
130 // Sort the pending timers into our list
131 while (pending) {
132 current = pending;
133 pending = pending->next;
134 SDL_AddTimerInternal(data, current);
135 }
136 freelist_head = NULL;
137 freelist_tail = NULL;
138
139 // Check to see if we're still running, after maintenance
140 if (!SDL_GetAtomicInt(&data->active)) {
141 break;
142 }
143
144 // Initial delay if there are no timers
145 delay = (Uint64)-1;
146
147 tick = SDL_GetTicksNS();
148
149 // Process all the pending timers for this tick
150 while (data->timers) {
151 current = data->timers;
152
153 if (tick < current->scheduled) {
154 // Scheduled for the future, wait a bit
155 delay = (current->scheduled - tick);
156 break;
157 }
158
159 // We're going to do something with this timer
160 data->timers = current->next;
161
162 if (SDL_GetAtomicInt(&current->canceled)) {
163 interval = 0;
164 } else {
165 if (current->callback_ms) {
166 interval = SDL_MS_TO_NS(current->callback_ms(current->userdata, current->timerID, (Uint32)SDL_NS_TO_MS(current->interval)));
167 } else {
168 interval = current->callback_ns(current->userdata, current->timerID, current->interval);
169 }
170 }
171
172 if (interval > 0) {
173 // Reschedule this timer
174 current->interval = interval;
175 current->scheduled = tick + interval;
176 SDL_AddTimerInternal(data, current);
177 } else {
178 if (!freelist_head) {
179 freelist_head = current;
180 }
181 if (freelist_tail) {
182 freelist_tail->next = current;
183 }
184 freelist_tail = current;
185
186 SDL_SetAtomicInt(&current->canceled, 1);
187 }
188 }
189
190 // Adjust the delay based on processing time
191 now = SDL_GetTicksNS();
192 interval = (now - tick);
193 if (interval > delay) {
194 delay = 0;
195 } else {
196 delay -= interval;
197 }
198
199 /* Note that each time a timer is added, this will return
200 immediately, but we process the timers added all at once.
201 That's okay, it just means we run through the loop a few
202 extra times.
203 */
204 SDL_WaitSemaphoreTimeoutNS(data->sem, delay);
205 }
206 return 0;
207}
208
209bool SDL_InitTimers(void)
210{
211 SDL_TimerData *data = &SDL_timer_data;
212
213 if (!SDL_ShouldInit(&data->init)) {
214 return true;
215 }
216
217 data->timermap_lock = SDL_CreateMutex();
218 if (!data->timermap_lock) {
219 goto error;
220 }
221
222 data->sem = SDL_CreateSemaphore(0);
223 if (!data->sem) {
224 goto error;
225 }
226
227 SDL_SetAtomicInt(&data->active, true);
228
229 // Timer threads use a callback into the app, so we can't set a limited stack size here.
230 data->thread = SDL_CreateThread(SDL_TimerThread, "SDLTimer", data);
231 if (!data->thread) {
232 goto error;
233 }
234
235 SDL_SetInitialized(&data->init, true);
236 return true;
237
238error:
239 SDL_SetInitialized(&data->init, true);
240 SDL_QuitTimers();
241 return false;
242}
243
244void SDL_QuitTimers(void)
245{
246 SDL_TimerData *data = &SDL_timer_data;
247 SDL_Timer *timer;
248 SDL_TimerMap *entry;
249
250 if (!SDL_ShouldQuit(&data->init)) {
251 return;
252 }
253
254 SDL_SetAtomicInt(&data->active, false);
255
256 // Shutdown the timer thread
257 if (data->thread) {
258 SDL_SignalSemaphore(data->sem);
259 SDL_WaitThread(data->thread, NULL);
260 data->thread = NULL;
261 }
262
263 if (data->sem) {
264 SDL_DestroySemaphore(data->sem);
265 data->sem = NULL;
266 }
267
268 // Clean up the timer entries
269 while (data->timers) {
270 timer = data->timers;
271 data->timers = timer->next;
272 SDL_free(timer);
273 }
274 while (data->freelist) {
275 timer = data->freelist;
276 data->freelist = timer->next;
277 SDL_free(timer);
278 }
279 while (data->timermap) {
280 entry = data->timermap;
281 data->timermap = entry->next;
282 SDL_free(entry);
283 }
284
285 if (data->timermap_lock) {
286 SDL_DestroyMutex(data->timermap_lock);
287 data->timermap_lock = NULL;
288 }
289
290 SDL_SetInitialized(&data->init, false);
291}
292
293static bool SDL_CheckInitTimers(void)
294{
295 return SDL_InitTimers();
296}
297
298static SDL_TimerID SDL_CreateTimer(Uint64 interval, SDL_TimerCallback callback_ms, SDL_NSTimerCallback callback_ns, void *userdata)
299{
300 SDL_TimerData *data = &SDL_timer_data;
301 SDL_Timer *timer;
302 SDL_TimerMap *entry;
303
304 if (!callback_ms && !callback_ns) {
305 SDL_InvalidParamError("callback");
306 return 0;
307 }
308
309 if (!SDL_CheckInitTimers()) {
310 return 0;
311 }
312
313 SDL_LockSpinlock(&data->lock);
314 timer = data->freelist;
315 if (timer) {
316 data->freelist = timer->next;
317 }
318 SDL_UnlockSpinlock(&data->lock);
319
320 if (timer) {
321 SDL_RemoveTimer(timer->timerID);
322 } else {
323 timer = (SDL_Timer *)SDL_malloc(sizeof(*timer));
324 if (!timer) {
325 return 0;
326 }
327 }
328 timer->timerID = SDL_GetNextObjectID();
329 timer->callback_ms = callback_ms;
330 timer->callback_ns = callback_ns;
331 timer->userdata = userdata;
332 timer->interval = interval;
333 timer->scheduled = SDL_GetTicksNS() + timer->interval;
334 SDL_SetAtomicInt(&timer->canceled, 0);
335
336 entry = (SDL_TimerMap *)SDL_malloc(sizeof(*entry));
337 if (!entry) {
338 SDL_free(timer);
339 return 0;
340 }
341 entry->timer = timer;
342 entry->timerID = timer->timerID;
343
344 SDL_LockMutex(data->timermap_lock);
345 entry->next = data->timermap;
346 data->timermap = entry;
347 SDL_UnlockMutex(data->timermap_lock);
348
349 // Add the timer to the pending list for the timer thread
350 SDL_LockSpinlock(&data->lock);
351 timer->next = data->pending;
352 data->pending = timer;
353 SDL_UnlockSpinlock(&data->lock);
354
355 // Wake up the timer thread if necessary
356 SDL_SignalSemaphore(data->sem);
357
358 return entry->timerID;
359}
360
361SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *userdata)
362{
363 return SDL_CreateTimer(SDL_MS_TO_NS(interval), callback, NULL, userdata);
364}
365
366SDL_TimerID SDL_AddTimerNS(Uint64 interval, SDL_NSTimerCallback callback, void *userdata)
367{
368 return SDL_CreateTimer(interval, NULL, callback, userdata);
369}
370
371bool SDL_RemoveTimer(SDL_TimerID id)
372{
373 SDL_TimerData *data = &SDL_timer_data;
374 SDL_TimerMap *prev, *entry;
375 bool canceled = false;
376
377 if (!id) {
378 return SDL_InvalidParamError("id");
379 }
380
381 // Find the timer
382 SDL_LockMutex(data->timermap_lock);
383 prev = NULL;
384 for (entry = data->timermap; entry; prev = entry, entry = entry->next) {
385 if (entry->timerID == id) {
386 if (prev) {
387 prev->next = entry->next;
388 } else {
389 data->timermap = entry->next;
390 }
391 break;
392 }
393 }
394 SDL_UnlockMutex(data->timermap_lock);
395
396 if (entry) {
397 if (!SDL_GetAtomicInt(&entry->timer->canceled)) {
398 SDL_SetAtomicInt(&entry->timer->canceled, 1);
399 canceled = true;
400 }
401 SDL_free(entry);
402 }
403 if (canceled) {
404 return true;
405 } else {
406 return SDL_SetError("Timer not found");
407 }
408}
409
410#else
411
412#include <emscripten/emscripten.h>
413#include <emscripten/eventloop.h>
414
415typedef struct SDL_TimerMap
416{
417 SDL_TimerID timerID;
418 int timeoutID;
419 Uint64 interval;
420 SDL_TimerCallback callback_ms;
421 SDL_NSTimerCallback callback_ns;
422 void *userdata;
423 struct SDL_TimerMap *next;
424} SDL_TimerMap;
425
426typedef struct
427{
428 SDL_TimerMap *timermap;
429} SDL_TimerData;
430
431static SDL_TimerData SDL_timer_data;
432
433static void SDL_Emscripten_TimerHelper(void *userdata)
434{
435 SDL_TimerMap *entry = (SDL_TimerMap *)userdata;
436 if (entry->callback_ms) {
437 entry->interval = SDL_MS_TO_NS(entry->callback_ms(entry->userdata, entry->timerID, (Uint32)SDL_NS_TO_MS(entry->interval)));
438 } else {
439 entry->interval = entry->callback_ns(entry->userdata, entry->timerID, entry->interval);
440 }
441 if (entry->interval > 0) {
442 entry->timeoutID = emscripten_set_timeout(&SDL_Emscripten_TimerHelper,
443 SDL_NS_TO_MS(entry->interval),
444 entry);
445 }
446}
447
448bool SDL_InitTimers(void)
449{
450 return true;
451}
452
453void SDL_QuitTimers(void)
454{
455 SDL_TimerData *data = &SDL_timer_data;
456 SDL_TimerMap *entry;
457
458 while (data->timermap) {
459 entry = data->timermap;
460 data->timermap = entry->next;
461 SDL_free(entry);
462 }
463}
464
465static SDL_TimerID SDL_CreateTimer(Uint64 interval, SDL_TimerCallback callback_ms, SDL_NSTimerCallback callback_ns, void *userdata)
466{
467 SDL_TimerData *data = &SDL_timer_data;
468 SDL_TimerMap *entry;
469
470 if (!callback_ms && !callback_ns) {
471 SDL_InvalidParamError("callback");
472 return 0;
473 }
474
475 entry = (SDL_TimerMap *)SDL_malloc(sizeof(*entry));
476 if (!entry) {
477 return 0;
478 }
479 entry->timerID = SDL_GetNextObjectID();
480 entry->callback_ms = callback_ms;
481 entry->callback_ns = callback_ns;
482 entry->userdata = userdata;
483 entry->interval = interval;
484
485 entry->timeoutID = emscripten_set_timeout(&SDL_Emscripten_TimerHelper,
486 SDL_NS_TO_MS(entry->interval),
487 entry);
488
489 entry->next = data->timermap;
490 data->timermap = entry;
491
492 return entry->timerID;
493}
494
495SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *userdata)
496{
497 return SDL_CreateTimer(SDL_MS_TO_NS(interval), callback, NULL, userdata);
498}
499
500SDL_TimerID SDL_AddTimerNS(Uint64 interval, SDL_NSTimerCallback callback, void *userdata)
501{
502 return SDL_CreateTimer(interval, NULL, callback, userdata);
503}
504
505bool SDL_RemoveTimer(SDL_TimerID id)
506{
507 SDL_TimerData *data = &SDL_timer_data;
508 SDL_TimerMap *prev, *entry;
509
510 if (!id) {
511 return SDL_InvalidParamError("id");
512 }
513
514 // Find the timer
515 prev = NULL;
516 for (entry = data->timermap; entry; prev = entry, entry = entry->next) {
517 if (entry->timerID == id) {
518 if (prev) {
519 prev->next = entry->next;
520 } else {
521 data->timermap = entry->next;
522 }
523 break;
524 }
525 }
526
527 if (entry) {
528 emscripten_clear_timeout(entry->timeoutID);
529 SDL_free(entry);
530 return true;
531 } else {
532 return SDL_SetError("Timer not found");
533 }
534}
535
536#endif // !SDL_PLATFORM_EMSCRIPTEN || !SDL_THREADS_DISABLED
537
538static Uint64 tick_start;
539static Uint32 tick_numerator_ns;
540static Uint32 tick_denominator_ns;
541static Uint32 tick_numerator_ms;
542static Uint32 tick_denominator_ms;
543
544#if defined(SDL_TIMER_WINDOWS) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
545#include <mmsystem.h>
546#define HAVE_TIME_BEGIN_PERIOD
547#endif
548
549static void SDL_SetSystemTimerResolutionMS(int period)
550{
551#ifdef HAVE_TIME_BEGIN_PERIOD
552 static int timer_period = 0;
553
554 if (period != timer_period) {
555 if (timer_period) {
556 timeEndPeriod((UINT)timer_period);
557 }
558
559 timer_period = period;
560
561 if (timer_period) {
562 timeBeginPeriod((UINT)timer_period);
563 }
564 }
565#endif // HAVE_TIME_BEGIN_PERIOD
566}
567
568static void SDLCALL SDL_TimerResolutionChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
569{
570 int period;
571
572 // Unless the hint says otherwise, let's have good sleep precision
573 if (hint && *hint) {
574 period = SDL_atoi(hint);
575 } else {
576 period = 1;
577 }
578 if (period || oldValue != hint) {
579 SDL_SetSystemTimerResolutionMS(period);
580 }
581}
582
583void SDL_InitTicks(void)
584{
585 Uint64 tick_freq;
586 Uint32 gcd;
587
588 if (tick_start) {
589 return;
590 }
591
592 /* If we didn't set a precision, set it high. This affects lots of things
593 on Windows besides the SDL timers, like audio callbacks, etc. */
594 SDL_AddHintCallback(SDL_HINT_TIMER_RESOLUTION,
595 SDL_TimerResolutionChanged, NULL);
596
597 tick_freq = SDL_GetPerformanceFrequency();
598 SDL_assert(tick_freq > 0 && tick_freq <= (Uint64)SDL_MAX_UINT32);
599
600 gcd = SDL_CalculateGCD(SDL_NS_PER_SECOND, (Uint32)tick_freq);
601 tick_numerator_ns = (SDL_NS_PER_SECOND / gcd);
602 tick_denominator_ns = (Uint32)(tick_freq / gcd);
603
604 gcd = SDL_CalculateGCD(SDL_MS_PER_SECOND, (Uint32)tick_freq);
605 tick_numerator_ms = (SDL_MS_PER_SECOND / gcd);
606 tick_denominator_ms = (Uint32)(tick_freq / gcd);
607
608 tick_start = SDL_GetPerformanceCounter();
609 if (!tick_start) {
610 --tick_start;
611 }
612}
613
614void SDL_QuitTicks(void)
615{
616 SDL_RemoveHintCallback(SDL_HINT_TIMER_RESOLUTION,
617 SDL_TimerResolutionChanged, NULL);
618
619 SDL_SetSystemTimerResolutionMS(0); // always release our timer resolution request.
620
621 tick_start = 0;
622}
623
624Uint64 SDL_GetTicksNS(void)
625{
626 Uint64 starting_value, value;
627
628 if (!tick_start) {
629 SDL_InitTicks();
630 }
631
632 starting_value = (SDL_GetPerformanceCounter() - tick_start);
633 value = (starting_value * tick_numerator_ns);
634 SDL_assert(value >= starting_value);
635 value /= tick_denominator_ns;
636 return value;
637}
638
639Uint64 SDL_GetTicks(void)
640{
641 Uint64 starting_value, value;
642
643 if (!tick_start) {
644 SDL_InitTicks();
645 }
646
647 starting_value = (SDL_GetPerformanceCounter() - tick_start);
648 value = (starting_value * tick_numerator_ms);
649 SDL_assert(value >= starting_value);
650 value /= tick_denominator_ms;
651 return value;
652}
653
654void SDL_Delay(Uint32 ms)
655{
656 SDL_SYS_DelayNS(SDL_MS_TO_NS(ms));
657}
658
659void SDL_DelayNS(Uint64 ns)
660{
661 SDL_SYS_DelayNS(ns);
662}
663
664void SDL_DelayPrecise(Uint64 ns)
665{
666 Uint64 current_value = SDL_GetTicksNS();
667 const Uint64 target_value = current_value + ns;
668
669 // Sleep for a short number of cycles when real sleeps are desired.
670 // We'll use 1 ms, it's the minimum guaranteed to produce real sleeps across
671 // all platforms.
672 const Uint64 SHORT_SLEEP_NS = 1 * SDL_NS_PER_MS;
673
674 // Try to sleep short of target_value. If for some crazy reason
675 // a particular platform sleeps for less than 1 ms when 1 ms was requested,
676 // that's fine, the code below can cope with that, but in practice no
677 // platforms behave that way.
678 Uint64 max_sleep_ns = SHORT_SLEEP_NS;
679 while (current_value + max_sleep_ns < target_value) {
680 // Sleep for a short time
681 SDL_SYS_DelayNS(SHORT_SLEEP_NS);
682
683 const Uint64 now = SDL_GetTicksNS();
684 const Uint64 next_sleep_ns = (now - current_value);
685 if (next_sleep_ns > max_sleep_ns) {
686 max_sleep_ns = next_sleep_ns;
687 }
688 current_value = now;
689 }
690
691 // Do a shorter sleep of the remaining time here, less the max overshoot in
692 // the first loop. Due to maintaining max_sleep_ns as
693 // greater-than-or-equal-to-1 ms, we can always subtract off 1 ms to get
694 // the duration overshot beyond a 1 ms sleep request; if the system never
695 // overshot, great, it's zero duration. By choosing the max overshoot
696 // amount, we're likely to not overshoot here. If the sleep here ends up
697 // functioning like SDL_DelayNS(0) internally, that's fine, we just don't
698 // get to do a more-precise-than-1 ms-resolution sleep to undershoot by a
699 // small amount on the current system, but SDL_DelayNS(0) does at least
700 // introduce a small, yielding delay on many platforms, better than an
701 // unyielding busyloop.
702 //
703 // Note that we'll always do at least one sleep in this function, so the
704 // minimum resolution will be that of SDL_SYS_DelayNS()
705 if (current_value < target_value && (target_value - current_value) > (max_sleep_ns - SHORT_SLEEP_NS)) {
706 const Uint64 delay_ns = (target_value - current_value) - (max_sleep_ns - SHORT_SLEEP_NS);
707 SDL_SYS_DelayNS(delay_ns);
708 current_value = SDL_GetTicksNS();
709 }
710
711 // We've likely undershot target_value at this point by a pretty small
712 // amount, but maybe not. The footgun case if not handled here is where
713 // we've undershot by a large amount, like several ms, but still smaller
714 // than the amount max_sleep_ns overshot by; in such a situation, the above
715 // shorter-sleep block didn't do any delay, the if-block wasn't entered.
716 // Also, maybe the shorter-sleep undershot by several ms, so we still don't
717 // want to spin a lot then. In such a case, we accept the possibility of
718 // overshooting to not spin much, or if overshot here, not at all, keeping
719 // CPU/power usage down in any case. Due to scheduler sloppiness, it's
720 // entirely possible to end up undershooting/overshooting here by much less
721 // than 1 ms even if the current system's sleep function is only 1
722 // ms-resolution, as SDL_GetTicksNS() generally is better resolution than 1
723 // ms on the systems SDL supports.
724 while (current_value + SHORT_SLEEP_NS < target_value) {
725 SDL_SYS_DelayNS(SHORT_SLEEP_NS);
726 current_value = SDL_GetTicksNS();
727 }
728
729 // Spin for any remaining time
730 while (current_value < target_value) {
731 SDL_CPUPauseInstruction();
732 current_value = SDL_GetTicksNS();
733 }
734}
diff --git a/contrib/SDL-3.2.8/src/timer/SDL_timer_c.h b/contrib/SDL-3.2.8/src/timer/SDL_timer_c.h
new file mode 100644
index 0000000..18d8171
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/timer/SDL_timer_c.h
@@ -0,0 +1,39 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#ifndef SDL_timer_c_h_
23#define SDL_timer_c_h_
24
25#include "SDL_internal.h"
26
27// Useful functions and variables from SDL_timer.c
28
29#define ROUND_RESOLUTION(X) \
30 (((X + TIMER_RESOLUTION - 1) / TIMER_RESOLUTION) * TIMER_RESOLUTION)
31
32extern void SDL_InitTicks(void);
33extern void SDL_QuitTicks(void);
34extern bool SDL_InitTimers(void);
35extern void SDL_QuitTimers(void);
36
37extern void SDL_SYS_DelayNS(Uint64 ns);
38
39#endif // SDL_timer_c_h_
diff --git a/contrib/SDL-3.2.8/src/timer/haiku/SDL_systimer.c b/contrib/SDL-3.2.8/src/timer/haiku/SDL_systimer.c
new file mode 100644
index 0000000..67447b1
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/timer/haiku/SDL_systimer.c
@@ -0,0 +1,43 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_TIMER_HAIKU
24
25#include <kernel/OS.h>
26
27
28Uint64 SDL_GetPerformanceCounter(void)
29{
30 return system_time();
31}
32
33Uint64 SDL_GetPerformanceFrequency(void)
34{
35 return SDL_US_PER_SECOND;
36}
37
38void SDL_SYS_DelayNS(Uint64 ns)
39{
40 snooze((bigtime_t)SDL_NS_TO_US(ns));
41}
42
43#endif // SDL_TIMER_HAIKU
diff --git a/contrib/SDL-3.2.8/src/timer/n3ds/SDL_systimer.c b/contrib/SDL-3.2.8/src/timer/n3ds/SDL_systimer.c
new file mode 100644
index 0000000..2e1881b
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/timer/n3ds/SDL_systimer.c
@@ -0,0 +1,43 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_TIMER_N3DS
24
25#include <3ds.h>
26
27
28Uint64 SDL_GetPerformanceCounter(void)
29{
30 return svcGetSystemTick();
31}
32
33Uint64 SDL_GetPerformanceFrequency(void)
34{
35 return SYSCLOCK_ARM11;
36}
37
38void SDL_SYS_DelayNS(Uint64 ns)
39{
40 svcSleepThread(ns);
41}
42
43#endif // SDL_TIMER_N3DS
diff --git a/contrib/SDL-3.2.8/src/timer/ps2/SDL_systimer.c b/contrib/SDL-3.2.8/src/timer/ps2/SDL_systimer.c
new file mode 100644
index 0000000..6d4ef04
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/timer/ps2/SDL_systimer.c
@@ -0,0 +1,50 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_TIMER_PS2
24
25#include "../SDL_timer_c.h"
26#include <stdlib.h>
27#include <time.h>
28#include <timer.h>
29#include <sys/time.h>
30
31
32Uint64 SDL_GetPerformanceCounter(void)
33{
34 return GetTimerSystemTime();
35}
36
37Uint64 SDL_GetPerformanceFrequency(void)
38{
39 return kBUSCLK;
40}
41
42void SDL_SYS_DelayNS(Uint64 ns)
43{
44 struct timespec tv;
45 tv.tv_sec = (ns / SDL_NS_PER_SECOND);
46 tv.tv_nsec = (ns % SDL_NS_PER_SECOND);
47 nanosleep(&tv, NULL);
48}
49
50#endif // SDL_TIMER_PS2
diff --git a/contrib/SDL-3.2.8/src/timer/psp/SDL_systimer.c b/contrib/SDL-3.2.8/src/timer/psp/SDL_systimer.c
new file mode 100644
index 0000000..0a8fdc8
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/timer/psp/SDL_systimer.c
@@ -0,0 +1,54 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_TIMER_PSP
24
25#include "../SDL_timer_c.h"
26#include <stdlib.h>
27#include <time.h>
28#include <sys/time.h>
29#include <pspthreadman.h>
30#include <psprtc.h>
31
32
33Uint64 SDL_GetPerformanceCounter(void)
34{
35 Uint64 ticks;
36 sceRtcGetCurrentTick(&ticks);
37 return ticks;
38}
39
40Uint64 SDL_GetPerformanceFrequency(void)
41{
42 return sceRtcGetTickResolution();
43}
44
45void SDL_SYS_DelayNS(Uint64 ns)
46{
47 const Uint64 max_delay = 0xffffffffLL * SDL_NS_PER_US;
48 if (ns > max_delay) {
49 ns = max_delay;
50 }
51 sceKernelDelayThreadCB((SceUInt)SDL_NS_TO_US(ns));
52}
53
54#endif // SDL_TIMER_PSP
diff --git a/contrib/SDL-3.2.8/src/timer/unix/SDL_systimer.c b/contrib/SDL-3.2.8/src/timer/unix/SDL_systimer.c
new file mode 100644
index 0000000..0f96319
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/timer/unix/SDL_systimer.c
@@ -0,0 +1,188 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_TIMER_UNIX
24
25#include <stdio.h>
26#include <sys/time.h>
27#include <unistd.h>
28#include <errno.h>
29
30#include "../SDL_timer_c.h"
31
32#ifdef SDL_PLATFORM_EMSCRIPTEN
33#include <emscripten.h>
34#endif
35
36/* The clock_gettime provides monotonous time, so we should use it if
37 it's available. The clock_gettime function is behind ifdef
38 for __USE_POSIX199309
39 Tommi Kyntola (tommi.kyntola@ray.fi) 27/09/2005
40*/
41/* Reworked monotonic clock to not assume the current system has one
42 as not all linux kernels provide a monotonic clock (yeah recent ones
43 probably do)
44 Also added macOS Monotonic clock support
45 Based on work in https://github.com/ThomasHabets/monotonic_clock
46 */
47#if defined(HAVE_NANOSLEEP) || defined(HAVE_CLOCK_GETTIME)
48#include <time.h>
49#endif
50#ifdef SDL_PLATFORM_APPLE
51#include <mach/mach_time.h>
52#endif
53
54// Use CLOCK_MONOTONIC_RAW, if available, which is not subject to adjustment by NTP
55#ifdef HAVE_CLOCK_GETTIME
56#ifdef CLOCK_MONOTONIC_RAW
57#define SDL_MONOTONIC_CLOCK CLOCK_MONOTONIC_RAW
58#else
59#define SDL_MONOTONIC_CLOCK CLOCK_MONOTONIC
60#endif
61#endif
62
63// The first ticks value of the application
64#if !defined(HAVE_CLOCK_GETTIME) && defined(SDL_PLATFORM_APPLE)
65mach_timebase_info_data_t mach_base_info;
66#endif
67static bool checked_monotonic_time = false;
68static bool has_monotonic_time = false;
69
70static void CheckMonotonicTime(void)
71{
72#ifdef HAVE_CLOCK_GETTIME
73 struct timespec value;
74 if (clock_gettime(SDL_MONOTONIC_CLOCK, &value) == 0) {
75 has_monotonic_time = true;
76 }
77#elif defined(SDL_PLATFORM_APPLE)
78 if (mach_timebase_info(&mach_base_info) == 0) {
79 has_monotonic_time = true;
80 }
81#endif
82 checked_monotonic_time = true;
83}
84
85Uint64 SDL_GetPerformanceCounter(void)
86{
87 Uint64 ticks;
88
89 if (!checked_monotonic_time) {
90 CheckMonotonicTime();
91 }
92
93 if (has_monotonic_time) {
94#ifdef HAVE_CLOCK_GETTIME
95 struct timespec now;
96
97 clock_gettime(SDL_MONOTONIC_CLOCK, &now);
98 ticks = now.tv_sec;
99 ticks *= SDL_NS_PER_SECOND;
100 ticks += now.tv_nsec;
101#elif defined(SDL_PLATFORM_APPLE)
102 ticks = mach_absolute_time();
103#else
104 SDL_assert(false);
105 ticks = 0;
106#endif
107 } else {
108 struct timeval now;
109
110 gettimeofday(&now, NULL);
111 ticks = now.tv_sec;
112 ticks *= SDL_US_PER_SECOND;
113 ticks += now.tv_usec;
114 }
115 return ticks;
116}
117
118Uint64 SDL_GetPerformanceFrequency(void)
119{
120 if (!checked_monotonic_time) {
121 CheckMonotonicTime();
122 }
123
124 if (has_monotonic_time) {
125#ifdef HAVE_CLOCK_GETTIME
126 return SDL_NS_PER_SECOND;
127#elif defined(SDL_PLATFORM_APPLE)
128 Uint64 freq = mach_base_info.denom;
129 freq *= SDL_NS_PER_SECOND;
130 freq /= mach_base_info.numer;
131 return freq;
132#endif
133 }
134
135 return SDL_US_PER_SECOND;
136}
137
138void SDL_SYS_DelayNS(Uint64 ns)
139{
140 int was_error;
141
142#ifdef HAVE_NANOSLEEP
143 struct timespec tv, remaining;
144#else
145 struct timeval tv;
146 Uint64 then, now, elapsed;
147#endif
148
149#ifdef SDL_PLATFORM_EMSCRIPTEN
150 if (emscripten_has_asyncify() && SDL_GetHintBoolean(SDL_HINT_EMSCRIPTEN_ASYNCIFY, true)) {
151 // pseudo-synchronous pause, used directly or through e.g. SDL_WaitEvent
152 emscripten_sleep(ns / SDL_NS_PER_MS);
153 return;
154 }
155#endif
156
157 // Set the timeout interval
158#ifdef HAVE_NANOSLEEP
159 remaining.tv_sec = (time_t)(ns / SDL_NS_PER_SECOND);
160 remaining.tv_nsec = (long)(ns % SDL_NS_PER_SECOND);
161#else
162 then = SDL_GetTicksNS();
163#endif
164 do {
165 errno = 0;
166
167#ifdef HAVE_NANOSLEEP
168 tv.tv_sec = remaining.tv_sec;
169 tv.tv_nsec = remaining.tv_nsec;
170 was_error = nanosleep(&tv, &remaining);
171#else
172 // Calculate the time interval left (in case of interrupt)
173 now = SDL_GetTicksNS();
174 elapsed = (now - then);
175 then = now;
176 if (elapsed >= ns) {
177 break;
178 }
179 ns -= elapsed;
180 tv.tv_sec = (ns / SDL_NS_PER_SECOND);
181 tv.tv_usec = SDL_NS_TO_US(ns % SDL_NS_PER_SECOND);
182
183 was_error = select(0, NULL, NULL, NULL, &tv);
184#endif // HAVE_NANOSLEEP
185 } while (was_error && (errno == EINTR));
186}
187
188#endif // SDL_TIMER_UNIX
diff --git a/contrib/SDL-3.2.8/src/timer/vita/SDL_systimer.c b/contrib/SDL-3.2.8/src/timer/vita/SDL_systimer.c
new file mode 100644
index 0000000..811a4bb
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/timer/vita/SDL_systimer.c
@@ -0,0 +1,51 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_TIMER_VITA
24
25#include "../SDL_timer_c.h"
26#include <stdlib.h>
27#include <time.h>
28#include <sys/time.h>
29#include <psp2/kernel/processmgr.h>
30
31
32Uint64 SDL_GetPerformanceCounter(void)
33{
34 return sceKernelGetProcessTimeWide();
35}
36
37Uint64 SDL_GetPerformanceFrequency(void)
38{
39 return SDL_US_PER_SECOND;
40}
41
42void SDL_SYS_DelayNS(Uint64 ns)
43{
44 const Uint64 max_delay = 0xffffffffLL * SDL_NS_PER_US;
45 if (ns > max_delay) {
46 ns = max_delay;
47 }
48 sceKernelDelayThreadCB((SceUInt)SDL_NS_TO_US(ns));
49}
50
51#endif // SDL_TIMER_VITA
diff --git a/contrib/SDL-3.2.8/src/timer/windows/SDL_systimer.c b/contrib/SDL-3.2.8/src/timer/windows/SDL_systimer.c
new file mode 100644
index 0000000..f6f4b15
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/timer/windows/SDL_systimer.c
@@ -0,0 +1,133 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_TIMER_WINDOWS
24
25#include "../../core/windows/SDL_windows.h"
26
27/* CREATE_WAITABLE_TIMER_HIGH_RESOLUTION flag was added in Windows 10 version 1803. */
28#ifndef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
29#define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x2
30#endif
31
32typedef HANDLE (WINAPI *CreateWaitableTimerExW_t)(LPSECURITY_ATTRIBUTES lpTimerAttributes, LPCWSTR lpTimerName, DWORD dwFlags, DWORD dwDesiredAccess);
33static CreateWaitableTimerExW_t pCreateWaitableTimerExW;
34
35typedef BOOL (WINAPI *SetWaitableTimerEx_t)(HANDLE hTimer, const LARGE_INTEGER *lpDueTime, LONG lPeriod, PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID lpArgToCompletionRoutine, PREASON_CONTEXT WakeContext, ULONG TolerableDelay);
36static SetWaitableTimerEx_t pSetWaitableTimerEx;
37
38static void SDL_CleanupWaitableHandle(void *handle)
39{
40 CloseHandle(handle);
41}
42
43static HANDLE SDL_GetWaitableTimer(void)
44{
45 static SDL_TLSID TLS_timer_handle;
46 HANDLE timer;
47
48 if (!pCreateWaitableTimerExW || !pSetWaitableTimerEx) {
49 static bool initialized;
50
51 if (!initialized) {
52 HMODULE module = GetModuleHandle(TEXT("kernel32.dll"));
53 if (module) {
54 pCreateWaitableTimerExW = (CreateWaitableTimerExW_t)GetProcAddress(module, "CreateWaitableTimerExW");
55 pSetWaitableTimerEx = (SetWaitableTimerEx_t)GetProcAddress(module, "SetWaitableTimerEx");
56 }
57 initialized = true;
58 }
59
60 if (!pCreateWaitableTimerExW || !pSetWaitableTimerEx) {
61 return NULL;
62 }
63 }
64
65 timer = SDL_GetTLS(&TLS_timer_handle);
66 if (!timer) {
67 timer = pCreateWaitableTimerExW(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
68 if (timer) {
69 SDL_SetTLS(&TLS_timer_handle, timer, SDL_CleanupWaitableHandle);
70 }
71 }
72 return timer;
73}
74
75static HANDLE SDL_GetWaitableEvent(void)
76{
77 static SDL_TLSID TLS_event_handle;
78 HANDLE event;
79
80 event = SDL_GetTLS(&TLS_event_handle);
81 if (!event) {
82 event = CreateEvent(NULL, FALSE, FALSE, NULL);
83 if (event) {
84 SDL_SetTLS(&TLS_event_handle, event, SDL_CleanupWaitableHandle);
85 }
86 }
87 return event;
88}
89
90Uint64 SDL_GetPerformanceCounter(void)
91{
92 LARGE_INTEGER counter;
93 const BOOL rc = QueryPerformanceCounter(&counter);
94 SDL_assert(rc != 0); // this should _never_ fail if you're on XP or later.
95 return (Uint64)counter.QuadPart;
96}
97
98Uint64 SDL_GetPerformanceFrequency(void)
99{
100 LARGE_INTEGER frequency;
101 const BOOL rc = QueryPerformanceFrequency(&frequency);
102 SDL_assert(rc != 0); // this should _never_ fail if you're on XP or later.
103 return (Uint64)frequency.QuadPart;
104}
105
106void SDL_SYS_DelayNS(Uint64 ns)
107{
108 HANDLE timer = SDL_GetWaitableTimer();
109 if (timer) {
110 LARGE_INTEGER due_time;
111 due_time.QuadPart = -((LONGLONG)ns / 100);
112 if (pSetWaitableTimerEx(timer, &due_time, 0, NULL, NULL, NULL, 0)) {
113 WaitForSingleObject(timer, INFINITE);
114 }
115 return;
116 }
117
118 const Uint64 max_delay = 0xffffffffLL * SDL_NS_PER_MS;
119 if (ns > max_delay) {
120 ns = max_delay;
121 }
122 const DWORD delay = (DWORD)SDL_NS_TO_MS(ns);
123
124 HANDLE event = SDL_GetWaitableEvent();
125 if (event) {
126 WaitForSingleObjectEx(event, delay, FALSE);
127 return;
128 }
129
130 Sleep(delay);
131}
132
133#endif // SDL_TIMER_WINDOWS