diff --git a/include/SDL3/SDL_timer.h b/include/SDL3/SDL_timer.h index fa8b2bb22b..56b22ec98f 100644 --- a/include/SDL3/SDL_timer.h +++ b/include/SDL3/SDL_timer.h @@ -115,8 +115,8 @@ extern SDL_DECLSPEC void SDLCALL SDL_Delay(Uint32 ms); * Wait a specified number of nanoseconds before returning. * * This function waits a specified number of nanoseconds before returning. It - * will attempt to wait as close to the requested time as possible, busy - * waiting if necessary, but could return later due to OS scheduling. + * waits at least the specified time, but possibly longer due to OS + * scheduling. * * \param ns the number of nanoseconds to delay. * @@ -124,6 +124,19 @@ extern SDL_DECLSPEC void SDLCALL SDL_Delay(Uint32 ms); */ extern SDL_DECLSPEC void SDLCALL SDL_DelayNS(Uint64 ns); +/** + * Wait a specified number of nanoseconds before returning. + * + * This function waits a specified number of nanoseconds before returning. It + * will attempt to wait as close to the requested time as possible, busy + * waiting if necessary, but could return later due to OS scheduling. + * + * \param ns the number of nanoseconds to delay. + * + * \since This function is available since SDL 3.1.5. + */ +extern SDL_DECLSPEC void SDLCALL SDL_DelayPrecise(Uint64 ns); + /** * Definition of the timer ID type. * diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index c20dff93ff..c60e930c03 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1177,6 +1177,7 @@ SDL3_0.0.0 { SDL_wcsstr; SDL_wcstol; SDL_StepBackUTF8; + SDL_DelayPrecise; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 556ac2cb74..c03a633f12 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1202,3 +1202,4 @@ #define SDL_wcsstr SDL_wcsstr_REAL #define SDL_wcstol SDL_wcstol_REAL #define SDL_StepBackUTF8 SDL_StepBackUTF8_REAL +#define SDL_DelayPrecise SDL_DelayPrecise_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 0486ce96aa..59c32eaaf5 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1208,3 +1208,4 @@ SDL_DYNAPI_PROC(wchar_t*,SDL_wcsnstr,(const wchar_t *a, const wchar_t *b, size_t SDL_DYNAPI_PROC(wchar_t*,SDL_wcsstr,(const wchar_t *a, const wchar_t *b),(a,b),return) SDL_DYNAPI_PROC(long,SDL_wcstol,(const wchar_t *a, wchar_t **b, int c),(a,b,c),return) SDL_DYNAPI_PROC(Uint32,SDL_StepBackUTF8,(const char *a, const char **b),(a,b),return) +SDL_DYNAPI_PROC(void,SDL_DelayPrecise,(Uint64 a),(a),) diff --git a/src/main/generic/SDL_sysmain_callbacks.c b/src/main/generic/SDL_sysmain_callbacks.c index aa59d32455..fb311fbd5b 100644 --- a/src/main/generic/SDL_sysmain_callbacks.c +++ b/src/main/generic/SDL_sysmain_callbacks.c @@ -65,7 +65,7 @@ int SDL_EnterAppMainCallbacks(int argc, char* argv[], SDL_AppInit_func appinit, } else { const Uint64 now = SDL_GetTicksNS(); if (next_iteration > now) { // Running faster than the limit, sleep a little. - SDL_DelayNS(next_iteration - now); + SDL_DelayPrecise(next_iteration - now); } else { next_iteration = now; // running behind (or just lost the window)...reset the timer. } diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 4d3ad51891..aec677630f 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -4948,7 +4948,7 @@ static void SDL_SimulateRenderVSync(SDL_Renderer *renderer) elapsed = (now - renderer->last_present); if (elapsed < interval) { Uint64 duration = (interval - elapsed); - SDL_DelayNS(duration); + SDL_DelayPrecise(duration); now = SDL_GetTicksNS(); } diff --git a/src/timer/SDL_timer.c b/src/timer/SDL_timer.c index 378832c62e..2d6ad8b025 100644 --- a/src/timer/SDL_timer.c +++ b/src/timer/SDL_timer.c @@ -657,6 +657,11 @@ void SDL_Delay(Uint32 ms) } void SDL_DelayNS(Uint64 ns) +{ + SDL_SYS_DelayNS(ns); +} + +void SDL_DelayPrecise(Uint64 ns) { Uint64 current_value = SDL_GetTicksNS(); Uint64 target_value = current_value + ns; diff --git a/test/testtimer.c b/test/testtimer.c index c98983c529..9523cd0dd9 100644 --- a/test/testtimer.c +++ b/test/testtimer.c @@ -178,6 +178,34 @@ int main(int argc, char *argv[]) /* Wait for the results to be seen */ SDL_Delay(1 * 1000); + /* Check accuracy of nanosecond delay */ + { + Uint64 desired_delay = SDL_NS_PER_SECOND / 60; + Uint64 actual_delay; + Uint64 total_overslept = 0; + + start = SDL_GetTicksNS(); + SDL_DelayNS(1); + now = SDL_GetTicksNS(); + actual_delay = (now - start); + SDL_Log("Minimum nanosecond delay: %" SDL_PRIu64 " ns\n", actual_delay); + + SDL_Log("Timing 100 frames at 60 FPS\n"); + for (i = 0; i < 100; ++i) { + start = SDL_GetTicksNS(); + SDL_DelayNS(desired_delay); + now = SDL_GetTicksNS(); + actual_delay = (now - start); + if (actual_delay > desired_delay) { + total_overslept += (actual_delay - desired_delay); + } + } + SDL_Log("Overslept %.2f ms\n", (double)total_overslept / SDL_NS_PER_MS); + } + + /* Wait for the results to be seen */ + SDL_Delay(1 * 1000); + /* Check accuracy of precise delay */ { Uint64 desired_delay = SDL_NS_PER_SECOND / 60;