Bitmap Font Rendering Optimization

Discuss about anything related to the Irrlicht Engine, or read announcements about any significant features or usage changes.
Post Reply
slavik262
Posts: 753
Joined: Sun Nov 22, 2009 9:25 pm
Location: Wisconsin, USA

Bitmap Font Rendering Optimization

Post by slavik262 »

I'd like to suggest a bit of optimization in the text rendering system. As of right now, the text rendering system (IGUIFont::draw and objects that use it such as IGUIStaticText) changes textures (using SetTexture in DirectX and a similar call in OGL) every single time a character is drawn, even if charactes are the same. This really increases the amount of GPU batches per frame, which really kills performance if you have a lot of text on your screen. I've been forced to use solid textures for all of my text labels to increase performance.

Would it be possible to change the IGUIFont rendering system so that it waits until all the draw() calls are made per frame before rendering the text? The system could keep track of all the characters it has to render in that frame, and then only have to switch textures for every different character on the screen. For example, if there are multiple instances of the character "A" on-screen, the system would only have to switch textures once to draw all the "A"s on screen, instead of switching textures for every single character.
CuteAlien
Admin
Posts: 9930
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Post by CuteAlien »

I'm not sure if we could wait for all draw calls. They might be needed earlier maybe. But a SetTexture call for every char indeed sounds bad.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Text should be rendered by the batch draw2d image call without changing the textures each character. Otherwise, there's an error in the implementation. OpenGL only implemented this for Irrlicht 1.7, but DirectX have had this already in 1.6
Collecting all calls might be problematic due to the render modes used in the GUI, at least it would require much testing and maybe also some additional administrative stuff. Maybe the full VBO GUI would be not much harder then.
slavik262
Posts: 753
Joined: Sun Nov 22, 2009 9:25 pm
Location: Wisconsin, USA

Post by slavik262 »

Let me run a quick test with PIX and get back to you guys.
slavik262
Posts: 753
Joined: Sun Nov 22, 2009 9:25 pm
Location: Wisconsin, USA

Post by slavik262 »

So I ran some tests, and here's the results I got.

First of all, hybrid is correct, and the implementation is working properly. Each call to IGUIText::draw() only creates one SetTexture() call.

Now onto performance:

I ran a test comparing the speed of rendering text using IGUIFont::draw() and using a solid texture. The code for drawing the text using IGUIFont is as follows:

Code: Select all

#include <irrlicht.h>

#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif

using namespace irr;
using namespace core;
using namespace video;
using namespace gui;

const SColor BG_COLOR(255, 0xe4, 0xe7, 0xec);

int main(void)
{
	IrrlichtDevice* device = createDevice(EDT_DIRECT3D9, dimension2d<u32>(800, 600), 32);
	if(device == NULL)
		return 1;

	IVideoDriver* driver = device->getVideoDriver();
	IGUIEnvironment* guienv = device->getGUIEnvironment();
	IGUIFont* font = device->getGUIEnvironment()->getSkin()->getFont();

	static wchar_t* str = L"I am Andrew Ryan and I am here to ask you a question:\n"
		L"Is a man not entitled to the sweat of his brow?\n\n"
		L"No, says the man in Washington; it belongs to the poor.\n"
		L"No, says the man in the Vatican; it belongs to God.\n"
		L"No, says the man in Moscow; it belongs to everyone.\n\n"
		L"I rejected those answers. Instead, I chose something different.\n"
		L"I chose the impossible. I chose...\n\n"
		L"Rapture.\n\n"
		L"A city where the artist would not fear the censor.\n"
		L"Where the scientist would not be bound by petty morality.\n"
		L"Where the great would not be constrained by the small.\n"
		L"And with the sweat of your brow,\n"
		L"Rapture can become your city as well.";

	static const u32 len = wcslen(str);

	rect<s32> fontRect(0, 0, 800, 600);

	guienv->addStaticText(str, fontRect);


	u16 framesSinceLastFPS = 0;

	while(device->run())
	{
		if(device->isWindowActive())
		{
			if(++framesSinceLastFPS == 20)
			{
				framesSinceLastFPS = 0;
				stringw cap(driver->getFPS());
				cap += " FPS, ";
				cap += len;
				cap += " characters";
				device->setWindowCaption(cap.c_str());
			}

			driver->beginScene(true, true, BG_COLOR);
			guienv->drawAll();
			driver->endScene();
		}
		else
			device->yield();
	}
	return 0;
}
The result (notice FPS):

Image

I then made a solid texture of the same text (512 x 512, made in Notepad and GIMP) to see how much faster it rendered. The code:

Code: Select all

#include <irrlicht.h>

#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif

using namespace irr;
using namespace core;
using namespace video;
using namespace gui;

const SColor BG_COLOR(255, 0xe4, 0xe7, 0xec);

int main(void)
{
	IrrlichtDevice* device = createDevice(EDT_DIRECT3D9, dimension2d<u32>(800, 600), 32);
	if(device == NULL)
		return 1;

	IVideoDriver* driver = device->getVideoDriver();
	IGUIEnvironment* guienv = device->getGUIEnvironment();
	
	IImage* textImage = driver->createImageFromFile("Text.png");
	ITexture* textTexture = driver->addTexture("Text Texture", textImage);
	textImage->drop();

	guienv->addImage(textTexture, vector2d<s32>(10, 10));


	u16 framesSinceLastFPS = 0;

	while(device->run())
	{
		if(device->isWindowActive())
		{
			if(++framesSinceLastFPS == 20)
			{
				framesSinceLastFPS = 0;
				stringw cap(driver->getFPS());
				cap += " FPS, ";
				cap += " Drawn with a solid texture";
				device->setWindowCaption(cap.c_str());
			}

			driver->beginScene(true, true, BG_COLOR);
			guienv->drawAll();
			driver->endScene();
		}
		else
			device->yield();
	}
	return 0;
}
And the result (notice the FPS again):

Image


Notice how large the difference is (the solid texture is almost 2.5 times faster). Now I know it takes time to clip out each character from the font texture, but can anything be done to speed this process up and lessen the gap between these two figures?


The reason I originally was concerned about the amount of SetTexture() calls was because of the project I'm working on. It's a GUI-heavy dashboard designer for a robot dashboard (sorry if you've heard this already; there's already been 2 or 3 threads about it). Anyways, here's the dashboard designer with a single control (a meter) added:

Image

Here's the data I gathered from PIX during that time:

Image

Notice that there are 69 texture calls.

Now, when I select the meter, the properties window fills up with controls to edit properties like so:

Image

Note that each static text label is a solid texture to increase performance. Here is PIX data from when the properties controls are in existence:

Image

Notice both the drop in FPS and the new SetTexture() call number (90 more than before). What could cause such a jump in texture changes, and therefore such a drop in performance? I didn't think that a few GUI elements were so performance intensive.

As far as setup goes,
Irrlicht 1.7.1
Direct3D 9
GeForce 260GTX
Windows 7 Pro
Lonesome Ducky
Competition winner
Posts: 1123
Joined: Sun Jun 10, 2007 11:14 pm

Post by Lonesome Ducky »

TOTALLY unrelated, but Bioshock? Good choice, good choice :lol:
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

The problem could be the mix of GUI elements and fonts, which are heavily mixed in the GUI drawing. I'll have to check if we can reduce these calls somehow. Otherwise we really have to batch things. However, z-order will be a problem.
Post Reply