diff --git a/examples/renderer/18-debug-text/debug-text.c b/examples/renderer/18-debug-text/debug-text.c index 53ef86ea9..62005e6bd 100644 --- a/examples/renderer/18-debug-text/debug-text.c +++ b/examples/renderer/18-debug-text/debug-text.c @@ -46,6 +46,8 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) /* This function runs once per frame, and is the heart of the program. */ SDL_AppResult SDL_AppIterate(void *appstate) { + const int charsize = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; + /* as you can see from this, rendering draws over whatever was drawn before it. */ SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); /* black, full alpha */ SDL_RenderClear(renderer); /* start with a blank canvas. */ @@ -63,7 +65,7 @@ SDL_AppResult SDL_AppIterate(void *appstate) SDL_SetRenderScale(renderer, 1.0f, 1.0f); SDL_RenderDebugText(renderer, 64, 350, "This only does ASCII chars. So this laughing emoji won't draw: 🤣"); - SDL_RenderDebugTextF(renderer, 0, 0, "This program has been running for %" SDL_PRIu64 " seconds.", SDL_GetTicks() / 1000); + SDL_RenderDebugTextFormat(renderer, (float) ((WINDOW_WIDTH - (charsize * 46)) / 2), 400, "(This program has been running for %" SDL_PRIu64 " seconds.)", SDL_GetTicks() / 1000); SDL_RenderPresent(renderer); /* put it all on the screen! */ diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 0aabb8f33..d3a816f5a 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -2530,8 +2530,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetRenderVSync(SDL_Renderer *renderer, int * * \since This function is available since SDL 3.1.6. * - * \sa SDL_RenderDebugTextF - * \sa SDL_RenderDebugTextV + * \sa SDL_RenderDebugTextFormat * \sa SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE */ extern SDL_DECLSPEC bool SDLCALL SDL_RenderDebugText(SDL_Renderer *renderer, float x, float y, const char *str); @@ -2539,9 +2538,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderDebugText(SDL_Renderer *renderer, flo /** * Draw debug text to an SDL_Renderer. * - * This function will render a printf() style format string to a renderer. Note - * that this is a convinence function for debugging, with severe limitations, - * and is not intended to be used for production apps and games. + * This function will render a printf()-style format string to a renderer. + * Note that this is a convinence function for debugging, with severe + * limitations, and is not intended to be used for production apps and games. * * For the full list of limitations and other useful information, * see SDL_RenderDebugText. @@ -2550,7 +2549,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderDebugText(SDL_Renderer *renderer, flo * \param x the x coordinate where the top-left corner of the text will draw. * \param y the y coordinate where the top-left corner of the text will draw. * \param fmt the format string to draw. - * \param ... format arguments + * \param ... additional parameters matching % tokens in the `fmt` string, if + * any. * \returns true on success or false on failure; call SDL_GetError() for more * information. * @@ -2559,38 +2559,9 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderDebugText(SDL_Renderer *renderer, flo * \since This function is available since SDL 3.1.7. * * \sa SDL_RenderDebugText - * \sa SDL_RenderDebugTextV * \sa SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE */ -extern SDL_DECLSPEC bool SDLCALL SDL_RenderDebugTextF(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) SDL_PRINTF_VARARG_FUNC(4); - -/** - * Draw debug text to an SDL_Renderer. - * - * This function will render a printf() style format string to a renderer. Note - * that this is a convinence function for debugging, with severe limitations, - * and is not intended to be used for production apps and games. - * - * For the full list of limitations and other useful information, - * see SDL_RenderDebugText. - * - * \param renderer the renderer which should draw the text. - * \param x the x coordinate where the top-left corner of the text will draw. - * \param y the y coordinate where the top-left corner of the text will draw. - * \param fmt the format string to draw. - * \param ap a variable argument lists representing the format arguments - * \returns true on success or false on failure; call SDL_GetError() for more - * information. - * - * \threadsafety This function should only be called on the main thread. - * - * \since This function is available since SDL 3.1.7. - * - * \sa SDL_RenderDebugText - * \sa SDL_RenderDebugTextF - * \sa SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE - */ -extern SDL_DECLSPEC bool SDLCALL SDL_RenderDebugTextV(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) SDL_PRINTF_VARARG_FUNCV(4); +extern SDL_DECLSPEC bool SDLCALL SDL_RenderDebugTextFormat(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) SDL_PRINTF_VARARG_FUNC(4); /* Ends C function definitions when using C++ */ #ifdef __cplusplus diff --git a/src/dynapi/SDL_dynapi.c b/src/dynapi/SDL_dynapi.c index 3229b5014..fa6872e3d 100644 --- a/src/dynapi/SDL_dynapi.c +++ b/src/dynapi/SDL_dynapi.c @@ -149,15 +149,29 @@ static void SDL_InitDynamicAPI(void); va_end(ap); \ return result; \ } \ - _static bool SDLCALL SDL_RenderDebugTextF##name(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ + _static bool SDLCALL SDL_RenderDebugTextFormat##name(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ { \ - bool result; \ + char buf[128], *str = buf; \ + int result; \ va_list ap; \ initcall; \ va_start(ap, fmt); \ - result = jump_table.SDL_RenderDebugTextV(renderer, x, y, fmt, ap); \ + result = jump_table.SDL_vsnprintf(buf, sizeof(buf), fmt, ap); \ va_end(ap); \ - return result; \ + if (result >= 0 && (size_t)result >= sizeof(buf)) { \ + str = NULL; \ + va_start(ap, fmt); \ + result = jump_table.SDL_vasprintf(&str, fmt, ap); \ + va_end(ap); \ + } \ + bool retval = false; \ + if (result >= 0) { \ + retval = jump_table.SDL_RenderDebugTextFormat(renderer, x, y, "%s", str); \ + } \ + if (str != buf) { \ + jump_table.SDL_free(str); \ + } \ + return retval; \ } \ _static void SDLCALL SDL_Log##name(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) \ { \ diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 7d4ee58d2..f3fe1c33c 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1207,8 +1207,7 @@ SDL3_0.0.0 { SDL_SetGPUAllowedFramesInFlight; SDL_RenderTextureAffine; SDL_WaitAndAcquireGPUSwapchainTexture; - SDL_RenderDebugTextF; - SDL_RenderDebugTextV; + SDL_RenderDebugTextFormat; # 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 6031adab1..12e77270c 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1232,5 +1232,4 @@ #define SDL_SetGPUAllowedFramesInFlight SDL_SetGPUAllowedFramesInFlight_REAL #define SDL_RenderTextureAffine SDL_RenderTextureAffine_REAL #define SDL_WaitAndAcquireGPUSwapchainTexture SDL_WaitAndAcquireGPUSwapchainTexture_REAL -#define SDL_RenderDebugTextF SDL_RenderDebugTextF_REAL -#define SDL_RenderDebugTextV SDL_RenderDebugTextV_REAL +#define SDL_RenderDebugTextFormat SDL_RenderDebugTextFormat_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index cedc79f58..189158744 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1239,6 +1239,5 @@ SDL_DYNAPI_PROC(bool,SDL_SetGPUAllowedFramesInFlight,(SDL_GPUDevice *a,Uint32 b) SDL_DYNAPI_PROC(bool,SDL_RenderTextureAffine,(SDL_Renderer *a,SDL_Texture *b,const SDL_FRect *c,const SDL_FPoint *d,const SDL_FPoint *e,const SDL_FPoint *f),(a,b,c,d,e,f),return) SDL_DYNAPI_PROC(bool,SDL_WaitAndAcquireGPUSwapchainTexture,(SDL_GPUCommandBuffer *a,SDL_Window *b,SDL_GPUTexture **c,Uint32 *d,Uint32 *e),(a,b,c,d,e),return) #ifndef SDL_DYNAPI_PROC_NO_VARARGS -SDL_DYNAPI_PROC(bool,SDL_RenderDebugTextF,(SDL_Renderer *a,float b,float c,SDL_PRINTF_FORMAT_STRING const char *d,...),(a,b,c,d),return) +SDL_DYNAPI_PROC(bool,SDL_RenderDebugTextFormat,(SDL_Renderer *a,float b,float c,SDL_PRINTF_FORMAT_STRING const char *d,...),(a,b,c,d),return) #endif -SDL_DYNAPI_PROC(bool,SDL_RenderDebugTextV,(SDL_Renderer *a,float b,float c,SDL_PRINTF_FORMAT_STRING const char *d,va_list e),(a,b,c,d,e),return) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 241916d44..1138464ca 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -5590,45 +5590,28 @@ bool SDL_RenderDebugText(SDL_Renderer *renderer, float x, float y, const char *s return result; } -bool SDL_RenderDebugTextF(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) +bool SDL_RenderDebugTextFormat(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) { va_list ap; - va_start(ap, fmt); - bool result = SDL_RenderDebugTextV(renderer, x, y, fmt, ap); + + // fast path to avoid unnecessary allocation and copy. If you're going through the dynapi, there's a good chance + // you _always_ hit this path, since it probably had to process varargs before calling into the jumptable. + if (SDL_strcmp(fmt, "%s") == 0) { + const char *str = va_arg(ap, const char *); + va_end(ap); + return SDL_RenderDebugText(renderer, x, y, str); + } + + char *str = NULL; + const int rc = SDL_vasprintf(&str, fmt, ap); va_end(ap); - return result; -} - -bool SDL_RenderDebugTextV(SDL_Renderer *renderer, float x, float y, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) -{ - // Probably for the best to check this here, so we don't do a bunch of string formatting before realizing the renderer isn't even valid... - CHECK_RENDERER_MAGIC(renderer, false); - - va_list apc; - va_copy(apc, ap); // vsnprintf mangles ap, so copy it so it can be used again later - int len = SDL_vsnprintf(NULL, 0, fmt, apc); - va_end(apc); - - if (len < 0) { - return SDL_SetError("Failed to format debug text"); - } - - char *buf = SDL_malloc(len + 1); - if (buf == NULL) { - return SDL_OutOfMemory(); - } - - len = SDL_vsnprintf(buf, len + 1, fmt, ap); - if (len < 0) { - SDL_free(buf); - return SDL_SetError("Failed to format debug text"); - } - - bool result = SDL_RenderDebugText(renderer, x, y, buf); - - SDL_free(buf); - - return result; + if (rc == -1) { + return false; + } + + const bool retval = SDL_RenderDebugText(renderer, x, y, str); + SDL_free(str); + return retval; }