SDL_GPUTransferBuffer doubts

Hello, using SDL3 GPU API here

I’m planning to have 3 GPU pipelines and 3 dynamic buffers: one for text, one for sprites, and one for lines. I want to be able to update the content of the buffers every frame. The documentation for SDL_CreateGPUTransferBuffer says: Download buffers can be particularly expensive to create. What about upload buffers?

Should I keep a single transfer buffer alive during the entire lifetime of my app?
Should I use a transfer buffer for the vertices and another one for the indices, and reuse these for every one of my 3 dynamic buffers?

If they are lightweight objects, is it ok to create and release them every frame? I will only use them to upload vertex and index data

Are you going to be drawing the exact same number of things every frame?

I don’t know. My idea is to create an app, something similar to a game level editor. So I would start with 0 vertices for the text, 0 vertices for the sprites, and 0 vertices for the lines buffers. The user should be able to add more elements during the lifetime of the app

In modern OpenGL I’ve always used the buffer orphaning technique and it worked perfectly. I’m trying to implement something similar with this new GPU api

In the GPU examples there’s a sprite batching example that keeps one GPU buffer and one transfer buffer, but it’s drawing the exact same number of sprites every frame. (explanation of how it works)

If the exact number of objects could change every frame, I’d keep a pool of fixed-size buffers, fill them up (adding more buffers to the pool if needed), then submit them for drawing.

It is probably not going to kill your app’s performance to allocate a handful of upload transfer buffers every frame.

edit: also, you almost certainly don’t need to use SDF font rendering for this. Something like stb_truetype coupled with stb_rect_pack is going to give you lightning fast font rendering (it’s just textured quads, you can use the same shader and pipeline you use for rendering sprites), whereas SDF font rendering is slower.

Also, since this is a game editor, you should strongly consider using Dear ImGui, which was specifically meant for in-engine game editors etc. It even has an SDL_GPU backend!

If you really want to do it yourself from the ground up, it would probably be a good idea to look at SDL_Renderer’s GPU backend and see how it handles transfer buffers etc.

No, sorry. I don’t like that kind of messy code :laughing:
Plus, I have written my own GUI already, I just need to port it from OpenGL 3.x

Nice idea! I will try that!

Good catch! Luckily I was experimenting with stb_truetype some time before, so I keep that code in another project. But I think it may be a good idea to keep the text in another GPU buffer anyways

Great! I’ll go forward with that
Thanks sjr!

Having looked at the GPU backend for SDL_Renderer, it seems what they are doing is keeping an all-encompassing array of vertices on the CPU side, and a single GPU buffer and transfer buffer. All vertices, regardless of type or size, go into the vert array. When it’s time to draw, if the GPU vertex buffer isn’t big enough, they release it and make a new one and a new transfer buffer (and it seems they may be leaking the old one :grimacing:).

The draw commands just specify offsets into this one buffer.

Today I tried creating a transfer buffer for each vertex and index array every frame, the code works but the screen is completely black. I guess it is too much work and the GPU doesn’t have time to process all those commands or something

That’s a great idea, since I’m using the same data struct for all of my vertices

Nice. And what is the starting size? Because the transfer buffer creation structs wants to know it. Let’s say you give it room for 10 vertices at the beginning of the app, more or less?

That’s really cool. Since I have 3 separate GPU buffers: text, sprites and lines, I should use offset 0 for text, then text data size (sizeof vertex struct * number of text vertices), and finally sprite data size (sizeof vertex struct * number of sprite vertices). Right?

That’s another doubt I have: can you create buffers and transfer buffers of size 0 at the start of the app? You see, I used to create my Vertex array object and buffers in OpenGL, but I gave them size 0. Then during updates, using the buffer orphaning mechanism, I gave each buffer the size of the data I had for that frame. I don’t know if this will work with the GPU api

So, some thoughts:

  1. Make sure you’re using cycling on the vertex buffer and transfer buffer
  2. Your starting buffer size should be something reasonable
  3. Since you’re only using one vertex buffer, it’s probably easier to just have one transfer buffer
  4. Since you’re only rendering quads, you could probably leave out the index buffer entirely.
  5. If you really want to have one, make sure you’re either adjusting the indices to account for all your vertices being in one buffer, or use the vertex_offset parameter when calling SDL_DrawGPUIndexedPrimitives()

If you’re gonna have One Vertex Buffer To Rule Them All, the layout should be:
offset 0: text vertices
offset (sizeof(vertex) * num_text_verts): sprite vertices
offset (sizeof(vertex) * (num_text_verts + num_sprite_verts)): lines

That’s the part I got wrong. Will try again. Thanks!

Interesting!

Documentation says nothing about giving buffers and transfer buffers a starting size of 0. But it doesn’t throw me any errors anyways. Curious

Yes, I set cycle to true at the mapping function and at the uploading function

So there isn’t really a downside to starting the buffer at a reasonable size. Your GPU vertex buffer should be the same size as your CPU-side vertex buffer.

With your CPU-side vertex buffer, every time you need to grow it you potentially have to allocate new space and copy the old contents over to the new buffer.

Preallocating the buffer even at a mere 512kb gives you room for 32768 2D vertices! (assuming 2 floats for position and 2 floats for texcoords)

In SDL_Renderer, every time it has to grow the vertex buffer it reallocates it to 2x its current size, to minimize the number of reallocations.

1 Like

Yes, and since I work with C++, this is done automagically for me (std::vector)

Right! I will try to use a starting size like that. Because this API doesn’t seems to work like old GL buffer orphaning at all! It certainly doesn’t like 0 sized buffers! (I’m curious if this also happens in Vulkan, since this is my selected backend under Linux). In any case, I think this should be added to the official GPU API documentation

That’s a good strategy! Thanks!


Using 1 KB buffer size for every gpu buffer, text works now. And it is correctly animated from left to right, so data is effectively updated every frame!

Now I just have to make sprites and lines work

It isn’t an issue of ease, but of speed. Potentially a new allocation and then having to copy the contents to the new, larger buffer.

Yes. Thanks for all your answers!