Direct2D performance issue

Started by
6 comments, last by karnalta 14 years, 5 months ago
Hello all, I am currently developping a small 3D engine under Direct3D 10/10.1 and I'd like to use Direct2D and DirectWrite to render the text showing the FPS. I am using a ID2D1RenderTarget to draw directly my text to the back buffer of my swapchain, but it cause my frame rate to drop down a lot. Where I was around 1500 fps with ID3DX10Font::DrawText method, I am now at 250-300 fps with Direct2D... Here is the code used to each frame rendering to draw the text : D2D1_SIZE_F renderTargetSize = m_pBackBufferRT->GetSize(); m_pBackBufferRT->BeginDraw(); m_pBackBufferRT->SetTransform(D2D1::Matrix3x2F::Identity()); std::wstring wstr = text; int textSize = wstr.length(); m_pBackBufferRT->DrawText( text, textSize, m_pTextFormat, D2D1::RectF(xStart, yStart, renderTargetSize.width, renderTargetSize.height), m_pDefaultBrush); HRESULT hr = m_pBackBufferRT->EndDraw(); return hr; Am I doing something wrong ? Is that normal to lost so much frame rate compared to the ID3DX10Font::DrawText method ? I have try with a ID2D1HwndRenderTarget using the Hwnd of my window but the text doesn't show up over the D3D viewport this way. I am certainly using it the wrong way. Thank for your help !
Advertisement
I suggest using DWrite to manage a texture cache of the font you want to use. Dwrite will simplify the process of saving a font into a texture via D2D. You can then use your texture font cache and supporting DWrite layout to keep track of the location of the characters.

All future text to be rendered in D3D can then use the font cache texture easily. DWrite will again be the best way to create layouts for your dynamic text. The dynamic layout will supply the supporting information such as pixel locations, hight, width, etc of each character which can be used to create dynamic vertex buffer full of quads for each character along with the texture coordinates from the font cache. This method should produce results similar to those of D3DX10Font.
I am not sure to understand, I think I already have my font stored into DWrite ?

m_pDWriteFactory->CreateTextFormat(fontName, NULL, fontWeight, DWRITE_FONT_style_NORMAL, DWRITE_FONT_STRETCH_NORMAL, fontSize, L"", &m_pTextFormat);

If you mean something else maybe do you have a short sample of your technique ?

Thank you.
As I understand it, DWrite is used for describing font details, but cannot actually render anything itself. Therefore, creating your DWrite font is only loading the character data for width/height and other attributes. There is no actual graphical data here.

Rendering a font using D2D is all CPU based. D2D has an extendable set of features, one of which is brushes, which are all written as CPU side code. This allows you to write your own brushes to describe how the font will be rendered/stroked. This extensibility also means that the rendering of fonts is slower than using something directly out of a texture cache on the GPU.

What I am suggesting is that you design a font cache manager, which will take a TextFormat and use it to render a character atlas to a texture. You can use the DWrite layout to easily get the atlas to fit into a square texture, or whatever layout shape you want. The DWrite layout will then be maintained since it provides the information you need about each characters placement in the font atlas.

Then, your application will submit text to be rendered to your class. The class will use a DWrite Layout of the text to determine all character positioning. Your class will then read the character position info, and use it to write per-character quads to a dynamic vertex buffer. Each quad will be given the coordinates for the appropriate character from the font atlas.

More detail:
This suggestion will only provide what is essentially fixed width characters in the font atlas. In reality, the rendering of text has special kerning rules for each font. The text is analyzed when rendered to provide the optimal spacing between characters for local appearance. An example of a possible kerning rule is to consider the string "To", some fonts will tuck the 'o' a little bit under the 'T'. Open your favorite text editor that supports fonts and try using a font with lots of decoration such as Palace Script MT. You'll find that the characters have quite a bit of custom overlap. So, obviously there is a trade off here. D3DX10Font will not obey any kerning rules for fonts, and so it's able to get a nice speed up at the expense of visual spacing quality.

I believe that you should be able to get both speed and quality if you make your own font atlas, and continue to use DWrite for doing dynamic layouts. The layout will follow kerning rules. The only thing you would then be missing would be other font rules where some characters pairs, such as "AE", or "SS" in German, have their own special merged character for rendering. The special analysis is another example of why the rendering is done on the CPU.

DWrite is supposed to support fonts for any language and so there are quite a large number of requirements in order to make the general solution. Obviously you can build some optimizations as a layer over D2D/DWrite in order to improve your own scenario.

[Edited by - DieterVW on November 11, 2009 5:46:47 PM]
Not to be too anal but how much text are you rendering in each of these cases? The difference in time between the two is less than 3.5 milliseconds. Unless that can be multiplied by some factor that is relative the the amount of text you are planning on rendering it's pretty much negligible.
To add to "lack o comments" comment:

FPS isn't a good measuring tool as it isn't linear. The drop from 1500 fps to 250 fps is ~ 3.33 milliseconds. This is the same as the drop between 60 and 50 fps.

Thus, the same initial drop of 1250 fps is exactly the same as the later drop of 10 fps.

I suggest you use ms/frame (divide 1 by your FPS as long as your fps isn't 0) as a measuring tool instead (or in addition to fps) as it's much more readable.

To hit 60 fps you have 16,66 ms/f to play with. To hit 30 fps you have 33,33 ms/f to play with. It's your call if it's worth losing 2.66-3.33 of those to the flexibility and feature set that Direct2D/DirectWrite offers.
I agree with the last two posters. It's a small FPS drop.

As for the solution, regardless of how you render text, you don't need to render it each frame. Text changes quite rarely, and usually looks the same over many frames, so it's easy to cache the text rendering results and use them again. A slower drawing method will then only affect frame speed rarely.
Sorry for my time to reply but it was night time for me ^^

Ok I now better understand what you mean DieterVW but it's seem a bit complex to setup considering I only need this to render FPS at the moment.

Then it's also true that my performance drop rate is only 3ms in reality, I should maybe not worry about it too much while my application is still at 300 FPS.

The FPS are calculated only once every second, so it could be a good solution to only draw the text one time per second. It's there a way to draw over the window litterally with Direct2D ? I mean not in the back buffer, but directly over the D3D viewport ? A solution of caching the text string is also a good idea but I am still quite new to DirectX and I am don't really now how to do it.

Thank.

This topic is closed to new replies.

Advertisement