FPS Drop With Many Controls

You discovered a bug in the engine, and you are sure that it is not a problem of your code? Just post it in here. Please read the bug posting guidelines first.
Post Reply
slavik262
Posts: 753
Joined: Sun Nov 22, 2009 9:25 pm
Location: Wisconsin, USA

FPS Drop With Many Controls

Post by slavik262 »

Hi all. I'm making a dashboard that receives data from a robot over a network and displays it in a GUI (readouts such as speed, status on certain components, etc.). Along with the dashboard itself, I'm making an "editor" mode that allows the user to create their own dashboard layouts without any changes to the code. I have a properties bar on the right of the screen that allows you to change properties of the selected control (a la Visual Studio's and other GUI designers). The problem is that when I select a control (and its properties controls show up in the properties bar), the frame rate drops a ridiculous amount. I made sure it's not anything overly stupid like a memory leak or something.

Without properties bar controls:

Image

With properties bar controls:

Image

Note the differences in FPS in the corners.

The drop only occurs when I run it in Direct3D (both 8 and 9). OpenGL doesn't cause the drop to occur at all.

The offending code is posted below. To give you some context, PropertiesControlBase is a dummy GUI element (a class that inherits IGUIElement) whose parent is the "Properties" window you see on the right of my screenshots. I'm using the dummy control so that when I implement a scrollbar later to scroll the properties up and down, the controls clip to the PropertiesControlBase rect, which starts below the window title bar so that the controls don't draw over the window title bar. Also, when I want to remove the properties controls, I can just call remove() on the PropertiesControlBase and it removes all of its children as well. When I comment out the code in this constructor, there's no drop in framerate when I select a control, so I'm positive that these elements are causing the problem.

Code: Select all


	PropertiesControlBase::PropertiesControlBase(IGUIEnvironment* env, IGUIElement* parent, const rect<s32>& rct,
		IDashEditControlBase* control) : IGUIElement(EGUIET_ELEMENT, env, parent, -1, rct), ctl(control)
	{
		props = ctl->getProperties();

		const s32 xPos = 10;
		const s32 yOffset = 8;
		const s32 ySubOff = 5;
		const s32 ctlSz = 22;
		s32 yPos = 0;


		env->addStaticText(L"Standard Properties:", rect<s32>(vector2d<s32>(xPos, yPos), dimension2d<s32>(190, ctlSz)),
			false, true, this);
		yPos += yOffset + ctlSz;
		env->addStaticText(L"Name:", rect<s32>(vector2d<s32>(xPos, yPos), dimension2d<s32>(50, ctlSz)),
			false, true, this);
		ctlName = env->addEditBox(props->name.c_str(), rect<s32>(vector2d<s32>(xPos + 60, yPos), dimension2d<s32>(100, ctlSz)),
			true, this, E_NAME);
		yPos += yOffset + ctlSz;
		env->addStaticText(L"Position:", rect<s32>(vector2d<s32>(xPos, yPos), dimension2d<s32>(100, ctlSz)),
			false, true, this);
		yPos += ySubOff + ctlSz;
		env->addStaticText(L"X:", rect<s32>(vector2d<s32>(xPos + 33, yPos), dimension2d<s32>(30, ctlSz)),
			false, true, this);
		ctlPosX = env->addSpinBox(stringw(props->rect.UpperLeftCorner.X).c_str(),
			rect<s32>(vector2d<s32>(xPos + 60, yPos), dimension2d<s32>(100, ctlSz)),
			true, this, E_POS_X);
		yPos += ySubOff + ctlSz;
		env->addStaticText(L"Y:", rect<s32>(vector2d<s32>(xPos + 33, yPos), dimension2d<s32>(30, ctlSz)),
			false, true, this);
		ctlPosY = env->addSpinBox(stringw(props->rect.UpperLeftCorner.Y).c_str(),
			rect<s32>(vector2d<s32>(xPos + 60, yPos), dimension2d<s32>(100, 20)),
			true, this, E_POS_Y);
		yPos += yOffset + ctlSz;
		env->addStaticText(L"Size:", rect<s32>(vector2d<s32>(xPos, yPos), dimension2d<s32>(100, ctlSz)),
			false, true, this);
		yPos += ySubOff + ctlSz;
		env->addStaticText(L"X:", rect<s32>(vector2d<s32>(xPos + 33, yPos), dimension2d<s32>(30, ctlSz)),
			false, true, this);
		ctlSzX = env->addSpinBox(stringw(props->rect.getWidth()).c_str(),
			rect<s32>(vector2d<s32>(xPos + 60, yPos), dimension2d<s32>(100, ctlSz)),
			true, this, E_SZ_X);
		yPos += ySubOff + ctlSz;
		env->addStaticText(L"Y:", rect<s32>(vector2d<s32>(xPos + 33, yPos), dimension2d<s32>(30, ctlSz)),
			false, true, this);
		ctlSzY = env->addSpinBox(stringw(props->rect.getHeight()).c_str(),
			rect<s32>(vector2d<s32>(xPos + 60, yPos), dimension2d<s32>(100, ctlSz)),
			true, this, E_SZ_Y);

		ctlPosX->setDecimalPlaces(0);
		ctlPosY->setDecimalPlaces(0);
		ctlSzX->setDecimalPlaces(0);
		ctlSzY->setDecimalPlaces(0);
	
		ctlPosX->setRange(0, (f32)kMaxPosX);
		ctlPosY->setRange(0, (f32)kMaxPosY);
		ctlSzX->setRange((f32)IDashControlBase::kMinCtlWidth, (f32)kMaxSzX);
		ctlSzY->setRange((f32)IDashControlBase::kMinCtlHeight, (f32)kMaxSzY);
	}
Also, on a slower system I tested this on (an Acer laptop with integrated graphics), there was a significant delay between selecting a control for the first time and drawing the elements in the "Properties" window when using D3D. This delay only happened the first time you selected a control, and after that there was no delay.

Based on what I'm seeing, I'd say it's some sort of bug involving the GUI system and D3D, perhaps when there's a decent amount of controls in the GUI environment.

The controls registered with the GUI environment when the problem occurs are as follows (so you can get an idea of how many there are):
  • 3 of the VU Meters you see in the edit pane. These are custom elements designed by me.
  • 2 IGUIWindow elements (the "Properties" window on the right and the "Packet Data" window on the bottom)
  • 1 VUMeterProperties element derived from the PropertiesControlBase class from which the above code was taken. This is a child of the "Properties" window.
  • The 13 elements inside the "Properties" window, which as you can see from the code above, are children of the VUMeterProperties element.
Can anyone replicate this? Obviously my temporary fix would be to use OpenGL, but I'd really like to find out why this is happening.

Edit: I tested it on a different machine, and the same problem occurred with OpenGL. I don't know what could be causing it.
slavik262
Posts: 753
Joined: Sun Nov 22, 2009 9:25 pm
Location: Wisconsin, USA

Post by slavik262 »

I ran some more tests, and it seems that the text rendering is slowing things down. The more text I put in the static text elements, the slower it renders. Why would text rendering take so long? I'm using a font I load at runtime using GUIEnvironment::getFont() then GUISkin::setFont(), but I don't see how that would affect things.

Doesn't text render using hardware textures? How would it slow down so much?

EDIT: Further tests show that the default font actually renders slower than the font I'm using.
Lonesome Ducky
Competition winner
Posts: 1123
Joined: Sun Jun 10, 2007 11:14 pm

Post by Lonesome Ducky »

If your text doesn't change often, you may consider rendering the text to a texture, and just drawing that texture instead. This is what I did in my program when I realized how slow text rendering was
slavik262
Posts: 753
Joined: Sun Nov 22, 2009 9:25 pm
Location: Wisconsin, USA

Post by slavik262 »

I started doing what you suggested, and performance is much better, but my question still is:

Why is text rendering so slow? Doesn't it use hardware textures to render?
Lonesome Ducky
Competition winner
Posts: 1123
Joined: Sun Jun 10, 2007 11:14 pm

Post by Lonesome Ducky »

It does, but it renders every letter separately. It constantly has to call to the graphics card to draw a single letter. Even this sentence right here has I'd guess around 100 letters, so rendering each letter in succession isn't going to be very fast.
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

However, even when showing the start screen in the meshviewer, my very old laptop renders at 300FPS. So I cannot see a reason why the framerate would drop from 1000 to 40.
slavik262
Posts: 753
Joined: Sun Nov 22, 2009 9:25 pm
Location: Wisconsin, USA

Post by slavik262 »

Neither can I, especially on a GTX260, but the drop in speed is directly proportional to the amount of letters I put in the static text elements. The problem disappeared when I switched over to using solid textures for each text element like Lonesome Ducky suggested.
slavik262
Posts: 753
Joined: Sun Nov 22, 2009 9:25 pm
Location: Wisconsin, USA

Post by slavik262 »

I'm still extremely displeased with the performance of text/digit rendering. Even after making all static labels solid textures to reduce the number of characters that need to be rendered (as suggested above), the performance is still killed whenever I get a bunch of controls displaying. Frames per second more than halves. Using PIX (Direct3D diagnostics software), I found that it's making 6 SetTexture calls when not showing the properties bar (refer to screenshot), and 96 when it is. Behavior is similar in OpenGL. I don't understand why 90 more calls are required just to draw a few digits. Does Irrlicht switch textures every time it draws a single character?

A screenshot of all the controls can be found here:

http://yfrog.com/2odashboardlp

Code for creating some of the controls is simply:

Code: Select all

env->addImage(vuPropsTxt, vector2d<s32>(xPos, yPos), true, this);
		yPos += yOffset + ctlHeight;
		env->addImage(currValueTxt, vector2d<s32>(xPos, yPos), true, this);
		ctlCurrValue = env->addSpinBox(stringw(vuProps->currValue).c_str(),
			rect<s32>(vector2d<s32>(xPos + xLIndent, yPos),
			dimension2d<s32>(ctlSWidth, ctlHeight)), true, this, E_CURR_VALUE);
		yPos += yOffset + ctlHeight;
		env->addImage(minValueTxt, vector2d<s32>(xPos, yPos), true, this);
		ctlMinValue = env->addSpinBox(stringw(vuProps->minValue).c_str(),
			rect<s32>(vector2d<s32>(xPos + xLIndent, yPos),
			dimension2d<s32>(ctlSWidth, ctlHeight)), true, this, E_MIN_VALUE);
		yPos += yOffset + ctlHeight;
		env->addImage(maxValueTxt, vector2d<s32>(xPos, yPos), true, this);
		ctlMaxValue = env->addSpinBox(stringw(vuProps->maxValue).c_str(),
			rect<s32>(vector2d<s32>(xPos + xLIndent, yPos),
			dimension2d<s32>(ctlSWidth, ctlHeight)), true, this, E_MAX_VALUE);
		yPos += yOffset + ctlHeight;
		env->addImage(vertTxt, vector2d<s32>(xPos, yPos), true, this);
		ctlVert = env->addComboBox(rect<s32>(vector2d<s32>(xPos + xLIndent, yPos),
			dimension2d<s32>(ctlSWidth, ctlHeight)), this, E_VERT);
		yPos += yOffset + ctlHeight;
		env->addImage(styleTxt, vector2d<s32>(xPos, yPos), true, this);
		ctlStyle = env->addComboBox(rect<s32>(vector2d<s32>(xPos + xLIndent, yPos),
			dimension2d<s32>(ctlSWidth, ctlHeight)), this, E_STYLE);
		yPos += yOffset + ctlHeight;
		env->addImage(lowRangeTxt, vector2d<s32>(xPos, yPos), true, this);
		yPos += ySubOff + ctlHeight;
		ctlLowColor = env->addButton(rect<s32>(vector2d<s32>(xPos + xSIndent, yPos),
			dimension2d<s32>(colorCtlWidth, ctlHeight)),
			this, E_LOW_COLOR, L"Change Color");
		yPos += yOffset + ctlHeight;
		env->addImage(midRangeTxt, vector2d<s32>(xPos, yPos), true, this);
		yPos += ySubOff + ctlHeight;
		ctlMidColor = env->addButton(rect<s32>(vector2d<s32>(xPos + xSIndent, yPos),
			dimension2d<s32>(colorCtlWidth, ctlHeight)),
			this, E_MID_COLOR, L"Change Color");
		yPos += ySubOff + ctlHeight;
		env->addImage(valueTxt, vector2d<s32>(xPos + xSIndent, yPos), true, this);
		ctlMidValue = env->addSpinBox(stringw(vuProps->midValue).c_str(),
			rect<s32>(vector2d<s32>(xPos + xLIndent, yPos),
			dimension2d<s32>(ctlSWidth, ctlHeight)), true, this, E_MID_VALUE);
		yPos += yOffset + ctlHeight;
		env->addImage(highRangeTxt, vector2d<s32>(xPos, yPos), true, this);
		yPos += ySubOff + ctlHeight;
		ctlHighColor = env->addButton(rect<s32>(vector2d<s32>(xPos + xSIndent, yPos),
			dimension2d<s32>(colorCtlWidth, ctlHeight)),
			this, E_HIGH_COLOR, L"Change Color");
		yPos += ySubOff + ctlHeight;
		env->addImage(valueTxt, vector2d<s32>(xPos + xSIndent, yPos), true, this);
		ctlHighValue = env->addSpinBox(stringw(vuProps->highValue).c_str(),
			rect<s32>(vector2d<s32>(xPos + xLIndent, yPos),
			dimension2d<s32>(ctlSWidth, ctlHeight)), true, this, E_HIGH_VALUE);
		yPos += yOffset + ctlHeight;
		env->addImage(bgColorTxt, vector2d<s32>(xPos, yPos), true, this);
		yPos += ySubOff + ctlHeight;
		ctlBgColor = env->addButton(rect<s32>(vector2d<s32>(xPos + xSIndent, yPos),
			dimension2d<s32>(colorCtlWidth, ctlHeight)),
			this, E_BG_COLOR, L"Change Color");
		yPos += yOffset + ctlHeight;

		ctlCurrValue->setDecimalPlaces(spinBoxDigits);
		ctlCurrValue->setRange(vuProps->minValue, vuProps->maxValue);
		ctlCurrValue->setStepSize(spinBoxStep);
		ctlMinValue->setDecimalPlaces(spinBoxDigits);
		ctlMinValue->setStepSize(spinBoxStep);
		ctlMinValue->setRange(absoluteCtlMin, vuProps->maxValue - spinBoxStep);
		ctlMaxValue->setDecimalPlaces(spinBoxDigits);
		ctlMaxValue->setStepSize(spinBoxStep);
		ctlMaxValue->setRange(vuProps->minValue + spinBoxStep, absoluteCtlMax);
		ctlMidValue->setDecimalPlaces(spinBoxDigits);
		ctlMidValue->setStepSize(spinBoxStep);
		ctlMidValue->setRange(vuProps->minValue + spinBoxStep, vuProps->highValue - spinBoxStep);
		ctlHighValue->setDecimalPlaces(spinBoxDigits);
		ctlHighValue->setStepSize(spinBoxStep);
		ctlHighValue->setRange(vuProps->midValue + spinBoxStep, vuProps->maxValue - spinBoxStep);
		ctlVert->addItem(L"Vert.");
		ctlVert->addItem(L"Horiz.");
		ctlStyle->addItem(L"Solid");
		ctlStyle->addItem(L"Bars");

		//Setup combo boxes
		ctlVert->setSelected(!vuProps->vert);

		if(vuProps->style == EDVS_SOLID)
			ctlStyle->setSelected(0);
		else
			ctlStyle->setSelected(1);


		addScrollBar(yPos);
Some of the variables are declared elsewhere, but you should get the idea. Again, can anyone replicate this or explain the issue?
Post Reply