Skip to main content
GameDev.net gamedev.net
🔒 Locked

D3D11 - Bitmap Font Rendering

Started by Endurion Jul 18, 2016 at 9:43 AM 10 replies 6.4k views
Original Post
Endurion
Endurion
Hi,

I'm currently working on an D3D11 module for my engine. I've got it to work fine mostly and I can now switch between DX8, DX9 and DX11 (with shaders).

However I have extreme performance problems with text rendering. I'm using bitmap fonts, and the naive approach, that worked fine for DX8 and DX9 used single quads per letter and DrawPrimitiveUP. Obviously that's not available anymore with DX11.

For DX11 my simple implementation creates a vertex buffer, fill it with data, upload it, render it and destroy it. One "optimization" was to reuse the created vertex buffer for every quad, but that didn't help much. It seems DrawPrimitiveUP does some heavy optimization under the hood, but the nature of that is unfortunately not known (to me).

What would be the best approach here? Really create vertex buffers for every string currently displayed and cache them?
I would prefer to stay with bitmap fonts (I'm aware of the downsides of foreign languages, etc..)
Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>
NightCreature83
NightCreature83

Do you create a buffer for each quad or are you using a buffer to hold multiple quads? (the text you wrote to me indicates that you create a buffer for each quad so just want to clear that up)

You should discard the content of the old buffer when you are filling it again with new data, you could create a quad data that contains all of the transformed vertex data and then just copy that into the vertex buffer for rendering. You would need a new VB if you change font or scaling size offcourse, and even that is fixable with instanced rendering.

QQemka
QQemka

I've has font problem several days ago and i decided to use Direct2D shared surface. It is not perfect but it works well.

dave
dave

Your approach should be fast enough, i use it for my entire gui draw which is rebuilt each frame.

QQemka
QQemka

Please report back if you manage to make it work. Tell me if you are somehow able to predict how much space will rendered text take, I cannot find out how to do it.

clashie
clashie

I just do the naive thing with 2D and it seems quite fast. Even on older hardware I could throw tens of thousands of them around like it was nothing.

I pre-fill a static 16 bit index buffer and create a dynamic vertex buffer for my sprites. I create the sprite geometry each frame and do some basic sorting and batching. It's pretty easy to batch large amounts of the sprites into a single draw call. Buffers for it are created once up front and destroyed only at the end.

Since the index buffer was 16 bit, I capped the number of sprites at 10922 per draw call.

(10922 * 4) * sizeof(Vertex2D)... which was 32. So 1398016 bytes for the vertex buffer. About 1.4mb.

QQemka
QQemka

I completely did not understand clashie... :) Are you able to change the text during runtime efficiently?

SeanMiddleditch
SeanMiddleditch
I highly recommend that you just grab the DirectXTK and use the helpers it provides for bitmap fonts and sprite rendering. If you need something special that it can't handle, at least look at how it solves the problem.

Note that it also provides a number of other very useful utilities like screen grab (screenshot) helpers, a decent math library, texture loading, simplified effects framework, and so on.
Sean Middleditch – Game Systems Engineer – Join my team!
Endurion
Endurion
A thanks for the recommendations. Looks like I'm off to implement nice batching then. I thought so, I was just befuddled at what a difference it makes, the little bit of text would cause such tremendous slowdowns.

@NightCreature83: I was creating a buffer for every quad. So that's quite a few reuses per frame for a few lines of text :)
Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>
NightCreature83
NightCreature83

A thanks for the recommendations. Looks like I'm off to implement nice batching then. I thought so, I was just befuddled at what a difference it makes, the little bit of text would cause such tremendous slowdowns.

@NightCreature83: I was creating a buffer for every quad. So that's quite a few reuses per frame for a few lines of text :)

Its the number of buffers here that are the problem, you are usually better of creating a fewer vertex buffers and having a few large ones than really small ones, the CPU overhead will cost more on the small buffers than on the large ones.

clashie
clashie

I completely did not understand clashie... :) Are you able to change the text during runtime efficiently?

Yes, my font is just packed into a texture. I wrote a tool that generates bitmap fonts and it also creates the glyph data I need alongside the actual texture.

So when I pass a character, it's indexes to an array with information for drawing it so I have the character's bounds, texture coordinates and whatever else I needed.

Endurion
Endurion
Got the batching thing more or less up and running, and it does offer quite a nice improvement. It's still not as fluid as the old DrawPrimitiveUP was, but there's still optimisation opportunity.

Thanks guys!
Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.