Fast text witin DirectX?

Started by
7 comments, last by DrunkenHyena 18 years, 7 months ago
Hi peez, I've been used to OpenGl.. but I've decided to currently "force" myself to learn DirectX. Here's a post from my webiste's news page which should further help show my problem... "eXodus, my latest project is still in progress... I've implemented many things... it's only just as of last night, became capable of rendering a triangle to the screen, after all this time. The reason it's taken so long to get to this, such a primitive stage, is simply, the base/common code. It now contains multithreading(Trying to implement the Win32 C API to co-operate nicely with C++ took some doing!), A fancy logging system, error checking, audio playback/saving, 2D image loading/saving, 2D, 3D and 4D vector types, decompression/compression of any file to .zip format ( special thanks to the SCArchive and CStream class), Generic NN AI with genetic evolution, mutlithreaded Winsock network server/client classes, resource manager template classes and of course, my current problem/work... font/text rendering. Fonts in DirectX just don't seem as fast... I'm too used to OpenGL and it's compiled display lists. Text rendering was fast and easy with OpenGL, yet with DirectX, I seem to have to do some testing... With OpenGL it was a simple case of loading in a image/texture containing each of the 256 character images, then looking at each one, determining their width, then creating a pre-compiled display list, which is then used to render each characer withing the alphabet and (pre-compiled) translating by that character's width, before rendering the next quad with the next char's tex coords. However, with DirectX (I'm still learning, so I dunno), there appears to be no way to pre-compile a list of commands and render them efficiently. So I've been scratching my head thinking of a way as to how to implement this concept within DirectX. Now, so far, I can see two possibilities (both of which appear to be not in the least as eligent as GL's method), 1. Render each char's quad and performing a matrix multiplication between each one (By the width+char spacing) or.. 2. Render a char's vertex buffer, then, for the next char, alter it's vertex buffer (Dynamic) along the X axis (Of width of previous char width + spacing) , then re send that through AGP. Which would be faster? 16 floats on CPU multiplication (Matrix) or sending 28float through AGP?) I dunno, either way appears slow compared to OpenGL's display lists... Does DirectX have some kind of "Display list" thing? I've yet to find it, I great;y miss OpenGL's ease of use and speed. :( " I'm stuck :( Does DirectX have some kindof pre-compiled "display list", in which method can I display text, fast? Any ideas, thoughts, links, suggestions would be greatly appreciated. I've even tried giving up and trying the D3DXText class, but it's just too slow compared to, *gasp*, even my "own, aka NeHe's" code with OpenGL for font rendering. EDIT: This post's title should read "Fast text rendering within DirectX", stoopid keyboard :) (Or is that "stoopid brain", i dunno.. I'm not a bad workman! EDIT2: Fixed post title! This forum rulez!
Advertisement
You seem to understand exactly how to do this from a general programming perspective, and you can definitely do it fast in DirectX.

What you want to do is create a dynamic vertex buffer (1000's of vertices) and then write all the quads at once into it. You reference each character by texture coordinates within your alphabet image, and then offset each quad by the width of the character (as you described). Then you call drawPrimitive and render all of the characters at once.

In fact, this is how you handle basically any 2D situation. Anything that uses just one texture can be drawn all in one go.

On your first frame you might use vertices 0-97. After you're done drawing, the next time around you lock the vertex buffer with the NOOVERWRITE flag (so you don't cause a stall) and then fill vertices 98-185 (for example). Continue until the vertex buffer is full, at which point you lock it with the DISCARD flag and start over. This is a fast process.

There is an nVidia PowerPoint somewhere that describes exactly how the vertex buffer flags should be used. This is VERY important to get right!

-Jay
I would not consider myself a graphics programmer and was actually more interested in the answer ... Neve the less, here is what I would do. Compile a vertex buffer per font for all the text that is currently on the screen. Each vertex buffer basically contains as many rects as there are characters using the corresponding font on the screen. Usually text changes rather infrequently so you may want to add an ID per rect that allowes you to identify which string that rect belongs to. You would then have an id = addText(string) and removeText(id) method rather then drawing the text each frame. You may also consider to only mark removed rects and remove them at the end of a frame in a single step.

-- Cheers
My answer is definitely a more general case 2D sprite-engine type setup, where you think you need/want dynamic buffers. My text class, for instance, just contains many instances of my sprite class (one sprite per char except space) as well as the spacing information and some other text methods. As a result the text can change color/scale/rotate or even scale as a group with no difference in code. If you want static (boring :) ) text only you definitely could consider a static vertex buffer which avoids the per-frame lock (and is VERY VERY fast). But you should seriously consider the general approach just because you can do some really cool stuff, it's easy, AND the only tradeoff is speed -- but remember we're already talking ridiculously fast speed to update and render a couple hundred vertices, assuming a reasonable amount of text.
Can you be more specific than "the D3DXText class"? D3DX has fairly fast 2D font support.. see D3DXCreateFont and ID3DXFont. It also has a function which creates a 3D mesh from a string.. see D3DXCreateText and ID3DXMesh. In your case, you'd want to use D3DXCreateFont. Also make sure you are using a semi-recent version of the DX9 SDK.. ID3DXFont in DX8 and early DX9 (before the first summer update a couple years ago) was kind of slow.

xyzzy
Quote:Original post by TerraX
audio playback/saving, 2D image loading/saving, 2D, 3D and 4D vector types

Just to make sure you are aware of the differences between DirectX and OpenGL, all of these above tasks have components within DirectX that should have made them easy. "audio playback/saving" is handled by either DirectSound or DirectMusic(or do you guys call it AudioVideoPlayback too?). "2D image loading/saving" are built directly into the D3DX library and handles every image type I have ever wanted to load. "2D, 3D and 4D vector types" (and matrices too) are also part of D3DX and have extensive functionality for all of your math related needs.

Just making sure you weren't unaware of the functionalities inside DirectX.
Turring Machines are better than C++ any day ^_~
Thanks Masterworks for your suggestion on dynamic vertex buffers, I'm pretty pleased with the performance..
40 lines of about 80 chars each = seems faster than my OpenGL dodgy code!
I crudly profiled the times for rendering a line of 80 or so chars..
the setting up of the vertex positions on the CPU was the slowest, ~0.003ms..
the rest was just stoopidly fast.. the buffer locking ~0.001ms and the actual rendering! ~0.0005ms!
I'm well impressed with DirectX's speed so far :)
thanks!

xyzzy00,
More specific with regards the the D3DX text class thing?.. Not really, I tried DirectX briefly a year or so ago.. (I believe I used DX8, maybe 9, I can't remember).
However, I like to write the stuff myself, I guess as coming over from OpenGL where, I kinda had to, I've gotten myself addicted to re-inventing the wheel :)

intrest86
I was aware of the D3DXwhatever functions/interfaces and I can see they save SO much work.. I really like the ability to load virtually every image format in existence with just a single line of code :) ... But as I'm new, I have no idea how to use these to load in an image from a buffer in memory.. and be able to access the texture's image data so that I can "draw" to it for creating Perlin noise etc... I guess there's a way, probably involving those "surface" things I read about.
I'm also aware that DirectX is not only a gfx API.. the audio component of eXodus for example, uses DSound and I've been using DInput alongside my OpenGL gfx projects for about 3-4 years. As I stated above to xyzzy00.. I just love re-inventing the wheel :)




Thanks everyone for da replies,
I'm starting to like DirectX's giving of control (and ability to blow your own head off!),
DaveC.
Quote:Original post by TerraX
Thanks Masterworks for your suggestion on dynamic vertex buffers, I'm pretty pleased with the performance..
40 lines of about 80 chars each = seems faster than my OpenGL dodgy code!
I crudly profiled the times for rendering a line of 80 or so chars..
the setting up of the vertex positions on the CPU was the slowest, ~0.003ms..
the rest was just stoopidly fast.. the buffer locking ~0.001ms and the actual rendering! ~0.0005ms!
I'm well impressed with DirectX's speed so far :)
thanks!


Glad to hear you got it working. If you get those flags (and everything else) right, its pretty fun stuff! Now that you've got all the complicated stuff working, you can make special effects and do neat tricks with almost no work. Here are some screenshots of my font system in action:





I have had a lot of fun NOT working with D3DXFont on this project! (Not that there is anything wrong with it.)

Many thanks to all those here who helped me make this game... and there are a bunch of you...
Quote:Original post by MasterWorks
I have had a lot of fun NOT working with D3DXFont on this project! (Not that there is anything wrong with it.)


D3DXFont is an EXCELLENT general purpose tool. But of course individual needs are sometimes outside of that (I prefer bitmap fonts). And sometimes it's just fun to write your own. :)

Nice screenshots, by the way.

Stay Casual,KenDrunken Hyena

This topic is closed to new replies.

Advertisement