Link to home
Start Free TrialLog in
Avatar of Valleriani
VallerianiFlag for Sweden

asked on

C++: DirectX Font/PrintText eats up alot of resources?

I have directx 9 added in my application, which works great except for a few things.

When printing text, it seems to eat up alot of FPS. Normally to draw it I will do something like:

void SF_TextBuffer::Draw(DxBaseFont* font, ulong frame){
      if (font)
            for (int i = 0; i < numLines(); i++)
                  if (frame - text[i].lastUpdate <(uint)(frameRate*20)){
                        const Line& cur = text[i];
                        font->printText(cur.text.c_str(), x0, y0 + font->metric.tmAscent*i/*font->desc.Height*i*/, cur.textColor);
                  }
}


I will attach the area for DirectX and printText as well. But I can't figure out how to optimize it, unless its not possible to optimize any more. Does anyone have any suggestions? Am I doing something incorrectly here? For the most case, it needs to display new messages constantly, since its part of a game.

(useCache is not enabled for this right now, doesn't help for speed anyways, it seems like the actual render to the screen thats slow.)

I'm only attaching the base file, can include more if you think its somewhere else causing issues. For the most case, the directX is basic, except for hardwarevertexrendering and basic DX enabled. I don't have anything special enabled like anti-alias etc.
//static IDirect3DDevice9* device = 0;
using Render::device;

using namespace Util;

class D3DXSpriteHelper: public DxResource{
public:
	ID3DXSprite* sprite;
	inline ID3DXSprite* getSprite(){
		if (this)
			return 0;
		return sprite;
	}

	HRESULT onLostDevice(){
		if (!sprite)
			return E_FAIL;
		return sprite->OnLostDevice();
	}

	HRESULT onResetDevice(){
		if (!sprite)
			return E_FAIL;
		return sprite->OnResetDevice();
	}

	D3DXSpriteHelper()
	:sprite(0){
		HRESULT hr = D3DXCreateSprite(Render::device, &sprite);
		hrcheck(hr, "D3DXCreateSprite failed!");
	}

	~D3DXSpriteHelper(){
		safeRelease(sprite);
	}
};

static D3DXSpriteHelper* helper = 0;

DxFont::~DxFont(void){
	safeDelete(cache);
	safeRelease(font);
}

DxFont::DxFont(const char* fontName, UINT height, UINT width, UINT weight, bool italic, bool createCache){
	cache = 0;
	font = 0;
	name = Str(fontName);
	resetOffset();

	HRESULT hr = 
	D3DXCreateFontA(
		device, height, width,
		weight, 0, italic,
		DEFAULT_CHARSET,
		OUT_DEFAULT_PRECIS,
		//CLEARTYPE_QUALITY,
		NONANTIALIASED_QUALITY,
		//DEFAULT_QUALITY,
		DEFAULT_PITCH|FF_DONTCARE,
		name.c_str(),
		&font
	);

	hrcheck(hr, "D3DXCreateFont failed");

	hr = font->GetDescA(&desc);
	hrcheck(hr, "font->GetDescA() failed");

	font->GetTextMetricsA(&metric);
	hrcheck(hr, "font->GetTextMetricsA() failed!");

	if (createCache)
		cache = new DxTextCache(this);
}

void DxFont::updateCache(){
	if (cache)
		cache->update();
}

void DxFont::printText(const char* str, int x, int y, D3DCOLOR color, bool useCache){
	if (useCache && cache)
		if (cache->drawText(str, x + xOffset, y + yOffset, color))
			return;
	RECT rect;
	rect.left = x + xOffset;
	rect.top = y + yOffset;
	rect.right = rect.left;
	rect.bottom = rect.top;
	HRESULT hr = font->DrawTextA(helper->getSprite(), str, -1, &rect, DT_NOCLIP, color);
	hrcheck(hr, "draw text failed!");
}

void DxFont::printText(const char* str, int x1, int y1, int x2, int y2, D3DCOLOR color){
	RECT rect;
	rect.left = x1 + xOffset;
	rect.top = y1 + yOffset;
	rect.right = x2 + xOffset;
	rect.bottom = y2 + yOffset;
	HRESULT hr = font->DrawTextA(helper->getSprite(), str, -1, &rect, 0, color);
	hrcheck(hr, "draw text failed!");
}

TextSize DxFont::getTextSize(const char* str){
	RECT rect;
	memset(&rect, 0, sizeof(rect));
	HRESULT hr = font->DrawTextA(0, str, -1, &rect, DT_CALCRECT, 0xFFFFFFFF);
	hrcheck(hr, "getTextSize failed!");
	return TextSize(rect.right - rect.left, rect.bottom - rect.top);
}

HRESULT DxFont::onLostDevice(){
	return font->OnLostDevice();
}

HRESULT DxFont::onResetDevice(){
	return font->OnResetDevice();
}

void DxFont::initHelper(){
	safeDelete(helper);
	helper = new D3DXSpriteHelper();
}

void DxFont::killHelper(){
	safeDelete(helper);
}

void DxFont::blitCache(int x, int y){
	cache->debugBlit(x, y);
}

void DxFont::printCacheStats(int x, int y, D3DCOLOR color){
	if (!cache)
		return;
	cache->printStats(x, y, color);
}

Open in new window

Avatar of Mark_FreeSoftware
Mark_FreeSoftware
Flag of Netherlands image

in the function getSprite, why do you return 0 when (this) is set?
seeing as it is a classfunction, and you are calling it with: helper->getSprite
(this) is valid and thus you are returning 0

according to this site, rendering to a sprite can speed up text rendering almost 4 times
http://www.toymaker.info/Games/html/text.html#optim

ASKER CERTIFIED SOLUTION
Avatar of Mark_FreeSoftware
Mark_FreeSoftware
Flag of Netherlands image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Valleriani

ASKER

Yes, I ment !this, must of gotten crossed wires there :)

Is there anywhere I can read up more about DX? You stated rendering to a sprite? How would this be possible overall (Anywhere to readup/coding examples/etc)

And I"ll check out the helper function now as well. Thanks!
Or is the helper = the rendering to sprite, which is actually looks like.

I haven't actually played with the helper before because I never fully understood it just yet.
Oh and it does look like its being called when DX is being initialized.

I see: DxFont::initHelper();

SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Avatar of ambience
ambience
Flag of Pakistan image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
10 Lines, and yes, its like a on-screen console. Even if you draw it every third frame, you're technically more or less going to have the FPS drop every second or third frame overall. Though I could see this working if you render 'other things' on other frames where the text isnt being rendered. Like:

1st frame: Draw Map
2nd frame: Draw Chat
3rd frame: Draw On-Screen Console (text)

That would I guess be a way, but I would think on its own you're not really gaining anything either or. Unless I'm incorrect, not sure though because I'm new to this!

And I actually just enabled the cache properly now, and the speed of the rendering has been increased by about 8x which is rather a huge surprise, I'm rather happy but curious about this...

I did try to change to:

            if (!this)
                  return 0;

But this ended up making the font not display at all. Everything went blank. I initalize via:
mainDxFont = new DxFont("Arial", 12, 0, FW_NORMAL, false, true);

And I'll post the textcache, but I'm not certain whats going on for that part!
you can just remove the if(!this) part and it would be ok.
The cache would be drawing the text to an off-screen buffer once and using it for subsequent draw operations - since that is just a buffer copy its magnitude times faster. If there is no caching then the system has to go the heavy duty route of using GDI routines to rasterize text