Helbreath Performance

All Helbreath Client Source Discussion here.
Jaap
Loyal fan
Posts: 390
Joined: Thu Apr 22, 2004 8:21 am

Post by Jaap »

As you all know, Helbreath is a pretty old game by now. I mean, it's 2005 now and the game was released in 1999. When you play the game, the performance of it is quite acceptable. But, let's get some facts straight here.

The game runs in a 640x480 (16bit) resolution mode. While in this resolution, my 2600 Mhz P4 with Radeon9600 GFX card usually gets around 80 FPS. This result is very slow compared to the resolution mode used on today's machines.

Now that I'm working on a 800x600 (still 16bit) resolution for the game, the performance goes down even more for obvious reasons. The results drop to an average of 60 FPS (inside a building around 70, outside with much objects around 50).

And if you push the resolution up to a 1024x768 (again 16bit) resolution, the results are again dropped significantly. The results drop to an average of 40 FPS.

Now the thing I wondered is why this happens. I mean, look at all of today's games. They can handle 1024x768 resolutions with 16bit mode without much problem (and we are talking about 3D games even!). Why is Helbreath so different? Why so "slow"?

The reason can be described rather easily. In fact, it's possible to describe the reason in just one word: Siementech. Because both the gameserver source and client source have leaked out to the world, it's now possible to look into their programming styles, habits and knowledge!

Being a student software engineer and with a great knowledge of (low level) C++ (not just bragging here, really!) I have reflected upon the source code. I came to a simple conclusion: the programmers at Siementech know what they are doing, but they create algorithms that bedroom coders or beginner-game programmers would create. They have often used algorithms that are described alot in the game developing communities that are for beginners/intermediates. There are alot of expert C++ programmers out there that already gave their opinions about these algorithms and even improved them! Almost always, such improvements require a deeper knowledge of low level programming - a thing that beginners and even intermediates do not know about or want to avoid. Experts avoid it too sometimes, when code readability is more important than performance - but performance is more important than readability for most games.

Siementech either have inexperienced C++ programmers, or they have decided to use code readability over performance. If you are wondering which of those is the case: just look at their code. They haven't used much of C++'s capabilities (especially when it comes to object-oriented use), and their code is hardly considered readable. I find it very difficult to track behaviour of things in the code and I'm deeply amazed when the CMapData class decides about the graphics and effectframecounts (just an example)!!

I'm shocked really, and I can only come to one conclusion: Siementech have inexperienced C++ programmers when it comes to programming games.


So, why is this information useful? Well, it isn't actually. But the following information is. I've described a few important performance issues in Helbreath. Some of these algorithms I describe below are used all over Helbreath (in the client and in the server).

Performance issue #1 - Unnecessairy POST-function checks
I bet Siementech never heard of assertions before. This is the especially the case in the server-side of Helbreath. Just take a good look at all the methods in the CGame class! I haven't really counted, but it could be well over 90% of the methods that checks if either m_pClientList[iClientH] isn't NULL, or similar cases. Alot of times a function is called that uses a CClient object in that list, which calls a function that uses the same object, which calls a function that uses the same object again and so on.. but in all these functions is checked wether or not the list entry is NULL. What a waste of processor cycles there!

This can be optimized by just removing the checks at the top of every function and by just giving the CClient object to the function as a parameter instead.

Performance issue #2 - Allocation before check
Siementech always allocated all variables at the top of the function, even before the checks (the if statements that verify if the call of the function was legal). So when a function check decides that the function cannot be executed, the variables are still allocated in the memory, which costs processor cycles. Sometimes buffers of numerous bytes are allocated before the decision is made wether or not is going to be used at all! This is the case in both the client and the server! Bad bad, bad Siementech!
Also, the POST-function checks are done in the function itself like this:

Code: Select all

void CGame::SomeMethod(int iClientH) {
	if (m_pClientList[iClientH] == NULL) return;
	// Function may be executed.
}
//The call to the function:
pGame->SomeMethod(iClientH);
And is then called by the program. So if the list entry is NULL, it means that the call to the SomeMethod function was unnecessairy. This is the case in nearly all functions in Siementech's code. This is better:

Code: Select all

void CGame::SomeMethod(int iClientH) {
	// Function assumes that iClientH is valid.
}
//The call to the function:
if (m_pClientList[iClientH] != NULL) {
	pGame->SomeMethod(iClientH);
}

Performance issue #3 - Numerous of unnecessairy switch/if statements
I've seen alot of this happening in both the client and server. I can't really explain it, but an example might just give you an idea of what I'm talking about:

Consider the following code fragment:

Code: Select all

switch (m_cGameMode) {
case DEF_GAMEMODE_ONVERSIONNOTMATCH:
	UpdateScreen_OnVersionNotMatch();

	break;
case DEF_GAMEMODE_ONCONNECTING:
	UpdateScreen_OnConnecting();

	break;
case DEF_GAMEMODE_ONMAINMENU:
	UpdateScreen_OnMainMenu();

	break;
.....
case DEF_GAMEMODE_ONLOADING:
	UpdateScreen_OnLoading(TRUE);

	break;
}
This piece of code is called every frame! So that means in every frame there is check to see what screen is currently to be updated. I don't know about you, but I'm shocked really! I wonder why they didn't use function pointers with an array here. Hmm gee, I guess they never heard of it before! A function pointer in this case would eliminate the check completely, so that more processor cycles are available for important things.

Just maintain a function pointer array with a UpdateScreen function for every screen (those already exist, but not the array). Then simply do:

Code: Select all

(*UpdateScreenPtr)();
Then you only need to bother updating this pointer at the SetGameMode function. And that's all, no unnecessairy checkups anymore (something you already know doesn't need to be checked again!).


Well my fingers hurt now, but there are alot more issues that can be changed quite easily. I just wanted to share this with all of you server/client developers out there.

If you have more suggestions and improvement, please feel free to post about them!
bone-you
Spamtastic
Posts: 1310
Joined: Wed Mar 16, 2005 3:12 am

Post by bone-you »

Good information to have ^^ nice work.
<img src='http://www.helbreathx.net/sig/sig.jpeg' border='0' alt='user posted image' /><br><a href='http://mafia.cheats4us.org/index.php?x=231030' target='_blank'>#1 on Mafia :D</a><br><!--QuoteBegin-Slipknight+--></div><table border='0' align='center' width='95%' cellpadding='3' cellspacing='1'><tr><td><b>QUOTE</b> (Slipknight)</td></tr><tr><td id='QUOTE'><!--QuoteEBegin-->100mb Internet, burstable too 10GB oc192<br>his speed can go up too 10gbs<br>...<br>Yes my car can have a top speed of 1000mph<!--QuoteEnd--></td></tr></table><div class='signature'><!--QuoteEEnd--><br>^^ I wonder where the retard went to.
Slayer
&lt;3 bd long time
Posts: 947
Joined: Thu Mar 17, 2005 9:08 pm

Post by Slayer »

Good work on that D...Jaap :lol:

Ga zo door! :P
<img src='http://i9.tinypic.com/2vs292h.jpg' border='0' alt='user posted image' />
Promote
Regular
Posts: 43
Joined: Thu Jul 07, 2005 7:17 pm

Post by Promote »

erm how do you up the resolution on hb O_o
Slayer
&lt;3 bd long time
Posts: 947
Joined: Thu Mar 17, 2005 9:08 pm

Post by Slayer »

Promote wrote: erm how do you up the resolution on hb O_o
If it was that easy it'd be done already, don't you think.. >_>
<img src='http://i9.tinypic.com/2vs292h.jpg' border='0' alt='user posted image' />
locobans
Outpost Junkie
Posts: 2264
Joined: Tue Jul 13, 2004 3:51 am
Location: Behind You
Contact:

Post by locobans »

Thanks...useful :)
QUOTE (ADDKiD @ Dec 1 2006, 4:01 PM) <br>You guys make me laugh alot, half the shit I say, is bullshit...<br><br><img src='http://img485.imageshack.us/img485/492/banssig1ng.gif' border='0' alt='user posted image' /><br><br><b>I see no changes at all, wake up in the morning and ask myself...<br>Is life worth living? Should I blast myself?</b><br><br><b><a href='http://2paclegacy.com' target='_blank'>2PacLegacy.com</a></b>
Jaap
Loyal fan
Posts: 390
Joined: Thu Apr 22, 2004 8:21 am

Post by Jaap »

No MMX alpha blending blitter

Siementech has not used the power of MMX technology to blit alpha blending sprites on the screen. This technique processes 4 bytes at once, instead of just one at a time, which increases overal performance alot.

It's better to check for MMX availability in the beginning of the program and if it is present, use it.

Also, alot of unnecessairy checks are done during the drawing of the sprites. There is alot of if-else going in that isnt necessairy. Example:

Code: Select all

if (m_bIsSurfaceEmpty == TRUE) {
	if ( _iOpenSprite() == FALSE ) return;
}
This is only executed when the sprite isnt loaded in the video memory (or system memory when its full). Usually only the first time this sprite is drawn, this check is useful. All the other times its just a waste of processor cycles.

One could remove this by using function pointers too. As long as the surface already exist in the memory, the function pointers points to drawing function (you could also make use of MMX/nonMMX here). If it isnt loaded yet, it points to the opensprite function first. The opensprite function sets the pointer to the correct drawing function.

This removes alot of checks and wasted cpu cycles, so speed improvements should be noticed!

FPS limit
Helbreath doesn't limit the FPS and the sending of network message, movement, etc (some time critical things) are based on framecount rather than time. This means computers with higher FPS can move faster, and lower FPS means moving slower.

Helbreath shouldn't base their movement on the FPS. They should multiply the movement with a speedfactor instead (that is calculated from the FPS value).
Ghaleon
Regular
Posts: 66
Joined: Sun Jul 17, 2005 7:10 am

Post by Ghaleon »

Can't wait till hb2 comes out with 800x600 :)
MOG Hackintosh
Loyal fan
Posts: 362
Joined: Sun May 30, 2004 5:34 am

Post by MOG Hackintosh »

Nice comments, and good reasons why it is best to write a whole new client than to base anything on simiancrap code.
<b>"I think im a hillbilly so pay me for my uber shiz" - Slipknight</b><span style='color:red'><br>yes and the dutch are always celebrating<br>thats holland:<br>"i got a new bicycle!" CHAMPAGNE! :D<br>"new marijuana bar opened" CHAMPAGNE :D <br>"it stopped raining for an hour" CHAMPAGNE! :D <br>"my condom didn't break" CHAMPAGNE! :D <br>"jews moved in next door" CHAMPAGNE! :D<br>"Look! A windmill!" CHAMPAGNE! :D</span>
Slayer
&lt;3 bd long time
Posts: 947
Joined: Thu Mar 17, 2005 9:08 pm

Post by Slayer »

Hopefully Jaap+Jameson(but he's on holiday or w/e) can code all this :]
<img src='http://i9.tinypic.com/2vs292h.jpg' border='0' alt='user posted image' />
MOG Hackintosh
Loyal fan
Posts: 362
Joined: Sun May 30, 2004 5:34 am

Post by MOG Hackintosh »

by the way...you really think 800 by 600 would be a good idea with those silly 256 colour sprites? Just seems like tying a mopar engine on top of a bicycle.
<b>"I think im a hillbilly so pay me for my uber shiz" - Slipknight</b><span style='color:red'><br>yes and the dutch are always celebrating<br>thats holland:<br>"i got a new bicycle!" CHAMPAGNE! :D<br>"new marijuana bar opened" CHAMPAGNE :D <br>"it stopped raining for an hour" CHAMPAGNE! :D <br>"my condom didn't break" CHAMPAGNE! :D <br>"jews moved in next door" CHAMPAGNE! :D<br>"Look! A windmill!" CHAMPAGNE! :D</span>
Slayer
&lt;3 bd long time
Posts: 947
Joined: Thu Mar 17, 2005 9:08 pm

Post by Slayer »

MOG Hackintosh wrote: by the way...you really think 800 by 600 would be a good idea with those silly 256 colour sprites? Just seems like tying a mopar engine on top of a bicycle.
Well, higher resolution would mean the sprites are smaller (on your screen that is). With a 19" TFT I see much pixels, and on my friends (Muismat from USA) widescreen laptop it looks like all chars are fat :lol:
<img src='http://i9.tinypic.com/2vs292h.jpg' border='0' alt='user posted image' />
Jaap
Loyal fan
Posts: 390
Joined: Thu Apr 22, 2004 8:21 am

Post by Jaap »

MOG Hackintosh wrote: by the way...you really think 800 by 600 would be a good idea with those silly 256 colour sprites? Just seems like tying a mopar engine on top of a bicycle.
Doesn't really affect anything.

Some of Helbreath's PAK files contain 8bit BMP's. However, when the client loads these BMP's, it is converted to 16bit DirectX surfaces anyway.

So the number of bits used to represent the sprites on the disk doesn't make any difference in speed or memory usage in Helbreath.
DarkieDuck
Loyal fan
Posts: 441
Joined: Wed Feb 18, 2004 7:10 am

Post by DarkieDuck »

omg jaap , stop wasting ur time silly!
hax
MOG Hackintosh
Loyal fan
Posts: 362
Joined: Sun May 30, 2004 5:34 am

Post by MOG Hackintosh »

At 800 by 600...all graphics would need to be scaled up 25%. The proportions would look similar but the sprites would have much higher detail, don't you think? I think that's what happened in SACRED. They did something along the lines of Diablo but at higher res.
<b>"I think im a hillbilly so pay me for my uber shiz" - Slipknight</b><span style='color:red'><br>yes and the dutch are always celebrating<br>thats holland:<br>"i got a new bicycle!" CHAMPAGNE! :D<br>"new marijuana bar opened" CHAMPAGNE :D <br>"it stopped raining for an hour" CHAMPAGNE! :D <br>"my condom didn't break" CHAMPAGNE! :D <br>"jews moved in next door" CHAMPAGNE! :D<br>"Look! A windmill!" CHAMPAGNE! :D</span>
Post Reply