Reputation: 11607
Premise: I render "sprites" based on a constant vertex buffer and use UBO data to position and size the quads on-the-fly in the vertex shader. This approach is separate from text rendering and not feasible for text strings because of reasons I try to explain below.
I'm using a glyph texture and one quad per character to render variable-width text (this is a full tutorial on bitmap fonts).
To render a whole line, I have to position the quads appropriately, and this means I have to set vertices accordingly. The problem here is that the n-th character position depends on all preceding characters in the string. Because of this, I've abandoned the idea to use a constant vertex buffer and position/size quad information with an UBO. This approach would just move the problem from the vertex buffer to the uniform buffer without much gain but needless additional complexity.
The vertex buffer contains the quads for all strings and has a maximum total size (say, 1024 characters/quads). With vkCmdDrawIndexedIndirect
I can then render just the right number of characters/quads.
The problem now is that the vertex buffer as a whole changes every time a single string (or even a character in a string) changes.
Say the vertex buffer contains quads for these 3 strings:
You have 10 hit points.Press left arrow to move camera left.This door is locked.
If I now loose a hit point, I want to change quads to render these 3 strings:
You have 9 hit points.Press left arrow to move camera left.This door is locked.
The 10
becoming a 9
causes all vertices to shift, and requires an upload of the whole vertex buffer.
Now consider that generally, many strings will change often if not every frame, and thus I'll end up with a vertex buffer that is transferred from host memory to device local memory every frame.
I've read in several questions/answers here on SO and other sources, that updating vertex buffers every frame is not really very smart, so... what other choices are there?
Even something like vkCmdUpdateBuffer
wouldn't help, I'd still have to update on the average 50% of the buffer.
Should I maybe have two types of labels? Like "static labels" that change not very often (maybe once per second) and a smaller set of "dynamic labels" which are supposed to change every frame and are managed on a completely separate basis? Maybe this "semantic" separation is necessary? Or is there a source (tutorial, document, whatever) that explains better how bitmap fonts should be used?
Or, maybe, is there a way to use a constant vertex buffer and an UBO that is organized somehow in a special way that I'm not aware of (e.G. keeping dynamic strings towards the end)?
I could add a C++ function that measures/estimates how static/dynamic the strings are, or even add a "dynamicity hint" to string rendering like this:
renderText("fontname", "string", x, y, MEDIUM_DYNAMICITY);
Do these ideas lead to feasible/senseful implementations? Or do I risk spending days implementing something that won't yield much fruitfulness?
Upvotes: 0
Views: 589
Reputation: 473447
Before deciding on an uploading scheme, first decide if you need uploading at all.
Check to see if your GPU has a pool of device local memory that is mappable and can be used as the source for a vertex buffer. This will be true of integrated GPUs. But even non-integrated AMD GPUs also tend to have ~256MB of memory which is both device-local and can be used as source data for vertex buffers.
If so, then just use that memory and write to it as needed.
Next, check to see if the GPU has any non-device-local memory that can be used as the source for a vertex buffer. Yes, the GPU reading from it will likely read a bit slower than reading from device-local memory, but it's just text. I seriously doubt that, even if you filled your screen up with text, the at most 5 thousand characters will be the thing that determines your application's performance.
In both of these cases, you're trying to avoid having to do any uploading. What you want is to just write the character data to some part of the memory, then use it those ranges of characters as buffer data. There are many schemes for this, but really, I wouldn't bother spending all that much time on it. Just write all of your character data, every frame, to different parts of the memory (so that you're not writing to areas that are being used by the previous frame).
Yes, this means that even if some piece of text, or all of the text, changes, you'll be writing new character data. But that really shouldn't be a problem. If you're using complex text layout, you can store the result of the layout process on the CPU, then read the glyph layout as needed when copying it into GPU-accessible storage.
And again, it's just text. It's not going to be what determines how fast your program is.
If your device can't do either of these, then you're going to have to deal with transferring data. But again, we're talking about text; it's not going to be all that much data per-frame. So just write everything to the staging buffer, make one transfer into the appropriate memory, and use it from there. Both the staging buffer and the destination storage need to be double-buffered to avoid overwriting data that is already in use.
Upvotes: 1