[SDL_ttf] Advice on best way to use with SDL_GPU

I’ve been tinkering around with SDL_ttf (v3.2.2) and have it displaying via my simplistic renderer using SDL GPU api. However I noticed it impacting frame rate. Currently, I’m just displaying some debugging text which gets updated every frame (showing delta time and other random things for testing).

Perusing the SDL_ttf codebase my use case looks quite expensive. Every time a TTF_Text string is updated it causes a layout update which does several reallocations and updating the atlas if necessary (plus more I’m sure). However, if I only update the TTF_Text object periodically then the frame rate is fine since the above takes place less frequently and I can reuse the same TTF_GPUAtlasDrawSequence for rendering.

Is there a way to use this for a more “immediate mode” style text rendering which is efficient (avoiding many reallocations per frame)?

Otherwise, I could generate my own atlas plus coordinate data but I wanted to make sure I didn’t miss something pre-existing in SDL_ttf for that.

Thanks

Can you report an issue on GitHub and include the sample you’re using to show the performance issues? There’s likely some improvements we can make for your use case.

I was getting an example working but before completing it I noticed the same frame rate issue. So, apologies for pointing my finger at SDL_ttf. Although I’m still concerned about the allocations… but that doesn’t appear to be my current issue.

I don’t understand why this minimal example which just shows a blank screen (no real rendering work happening yet) exhibits the issue. Mimics the poor timing I’m experiencing in my actual project which didn’t used to be there. Error handling omitted for brevity.

I’ve been using SDL_WaitAndAcquireGPUSwapchainTexture for a bit but never had hitches while rendering a few glTF models. The hitching started about the time I decided to try out SDL_ttf (a week or so ago) but maybe something with my system changed. Maybe libSDL updated?

Also, I don’t see an issue when playing a game on steam so I’d like to claim my video driver is fine but that might not be hitting the same combination of things.

I’m using SDL v3.4.8 on Arch with Wayland and mesa (radv).

#include <cstdio>

#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>

static TTF_TextEngine *text_engine = 0;
static TTF_Font *font = 0;
static TTF_Text *text = 0;

static SDL_Window *window = 0;
static SDL_GPUDevice *device = 0;
static SDL_GPUShader *font_vert_shader = 0;
static SDL_GPUShader *font_frag_shader = 0;
static SDL_GPUGraphicsPipeline *font_pipeline = 0;

static SDL_GPUBuffer *font_vertex_buffer = 0;
static SDL_GPUBuffer *font_index_buffer = 0;
static SDL_GPUTransferBuffer *font_xfer_buffer = 0;

static Uint64 curr_ticks = 0;
static Uint64 prev_ticks = 0;
static float delta_time = 0.0f;
static bool running = false;

static void HandleInit();
static void HandleEvents();
static void HandleRender();

int main(int argc, const char **argv) {
    SDL_Init(SDL_INIT_VIDEO);
    TTF_Init();

    text_engine = TTF_CreateSurfaceTextEngine();
    font = TTF_OpenFont("assets/fonts/JetBrainsMono-Regular.ttf", 24);
    text = TTF_CreateText(text_engine, font, "", 0);

    window = SDL_CreateWindow("SDL_ttf test", 1280, 720, 0);
    device = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV, true, "vulkan");

    SDL_ClaimWindowForGPUDevice(device, window);
    HandleInit();

    running = true;

    while (running) {
        prev_ticks = curr_ticks;
        curr_ticks = SDL_GetTicksNS();
        delta_time = float(curr_ticks - prev_ticks) / 1000000000.0f;

        printf("delta time: %.8f\n", delta_time);

        HandleEvents();
        HandleRender();
    }

    SDL_DestroyWindow(window);
    TTF_DestroyText(text);
    TTF_CloseFont(font);
    TTF_DestroySurfaceTextEngine(text_engine);
    TTF_Quit();

    return 0;
}

static void HandleInit() {}

static void HandleEvents() {
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        if (event.type == SDL_EVENT_QUIT) {
            running = false;
        }
    }
}

static void HandleRender() {
    SDL_GPUCommandBuffer *main_command_buffer = SDL_AcquireGPUCommandBuffer(device);
    SDL_GPUCommandBuffer *xfer_command_buffer = SDL_AcquireGPUCommandBuffer(device);

    SDL_GPUTexture *swapchain_texture;
    SDL_WaitAndAcquireGPUSwapchainTexture(main_command_buffer, window, &swapchain_texture, 0, 0);

    SDL_SubmitGPUCommandBuffer(xfer_command_buffer);
    SDL_SubmitGPUCommandBuffer(main_command_buffer);
}

Given this minimal example, I would expect easily to maintain 0.01667sec per frame (vsync by default from my understanding). However the timings are all over the place. Below a sample from my super advanced printf debugging.

delta time: 0.02856672
delta time: 0.01667805
delta time: 0.00479066
delta time: 0.02871928
delta time: 0.01660481
delta time: 0.00474674
delta time: 0.02856635
delta time: 0.01654240
delta time: 0.00482557
delta time: 0.02850081
delta time: 0.01666869
delta time: 0.00487760
delta time: 0.02846590
delta time: 0.01679120
delta time: 0.00478924
delta time: 0.02842702
delta time: 0.01665459
delta time: 0.00492683
delta time: 0.02875141
delta time: 0.01664690

Anything stand out or recommendations?

FWIW, when I started the project I was using SDL v3.4.4. So in that time span my system went from v3.4.4 → v3.4.8.

I think the main issue was me using vscodium. I was getting a lot more hitches when executing the program while vscodium is running. My timings are much better now that its closed.

I reworked my test code into a crude hitch counter and ran that against SDL 3.4.4 (built from source) vs 3.4.8 (package manager provided). Basically a “hitch” being any frame delta >= ~0.020.

They both average about the same (~9 frame hitches within 30sec). But since there are so many things involved (not just SDL) I don’t think my crude test really explains much. Could be video driver related (package manager updated mesa around the same time as SDL v3.4.8).

I just know that whatever has change its way worse when vscodium is running at the same time. For example, I just re-ran the test with vscodium running and I had 416 hitches in 30sec.

SDL version: 3.4.4
frame count: 1800
hitch count: 416
ticks (ns): 30166370176
ticks (ms): 30166

vs with vscodium killed off

SDL version: 3.4.4
frame count: 1800
hitch count: 5
ticks (ns): 30118768553
ticks (ms): 30118

With that said, sorry for the noise as this probably isn’t SDL related.