How do YOU optimize your game?

Luke Peña

Member
So right now, I'm actually sitting down to read through the entire Game Maker manual. In one of the early parts about debugging I think, it mentions that changing global draw settings - like font, color, etc - will slow the game down. This would mean that using a functions like draw_set_colour to change the draw color of a vertex is SLOWER than just using draw_vertex_colour, right?

The biggest drains according to the debug tools is (obviously) the draw event, bumping my game down to about 105 fps, which is less overhead than I'm comfortable with. I can usually figure out how to cut out excess in my logic code. But the actual drawing of the game has always been problematic for me.

TLDR: There must be all sorts of hidden ways to optimize the game. So my question is what things do all of you do to optimize your game's performance? (Especially visually!)
 
A

Anomaly

Guest
If been reading a lot about using vector graphics instead of the usual raster type pixel based graphics for sprites.
 

Hyomoto

Member
Well, optimization is a funny thing because you can probably get away with not doing a lot of it. Then there are times where it's vital. In my case I knew the performance on the world map was quite poor, but I got good results on my various test computers. However, it turns out that older iGPU's absolutely fell apart trying to render it. So, my solution was to cache the entire map and only redraw it on demand. As a nice side effect this means that all maps run at generally the same speed and it opens up a lot of overhead.

Granted I think once you've been doing anything for a while you start putting together optimizations unconsciously. My big habits are to cache things that don't change often or use a var whenever a calculated value will be used more than once. How much this saves I don't know, but I feel like it keeps things running as smooth as I can.
 
M

Matt Hawkins

Guest
If you run your game in debug mode you can see which objects might need optimizing
 

True Valhalla

Full-Time Developer
GMC Elder
Limiting excessive color/alpha fades is the main optimization that I focus on, as well as limiting texture page swaps. Drawing fonts is also performance intensive, in my experience, so I'll occasionally opt to format text as a sprite when it makes sense.
 

Smiechu

Member
Most important seams to be preparing all the logic, calculations and loops in the step (or even better step end) event...

Regarding your concern about color/alpha switching. I also worry about that, but the debugger is not pointing those functions as critical... Problematic by the debugger seam to be the draw_text, and in my case draw_sprite_part_ext...

The draw_text function is nothing else as looped draw_sprite function, if you look at the texture pages you can see that all fonts are converted into sprites...
So if you have a 20 character text to draw then it's equivalent to 20 draw_sprite function executions...
 

True Valhalla

Full-Time Developer
GMC Elder
Regarding your concern about color/alpha switching. I also worry about that, but the debugger is not pointing those functions as critical...
At least with the HTML5 export, these can easily be the source of massive performance issues.

So if you have a 20 character text to draw then it's equivalent to 20 draw_sprite function executions...
Correct. Then perhaps add some questionable "text shadow" code and you have a recipe for disaster.
 
Last edited:

Luke Peña

Member
Thanks to your suggestions and a little bit of awareness, I've quickly achieved an average FPS of 175, compared with earlier at 105. Which is awesome! And this is only by tweaking the major objects. Thanks guys!

About the draw_text thing. Is there a way to get around this massive drain? I tried to make a Paper Mario style textbox system with the ability to apply jitter and different effects to the individual letters. This meant each letter had to be a separate draw_text event. Sooo 200 characters = draw_text*200. You can imagine how that went.

What would you guys suggest for a work around in THAT kind of situation?
 

Smiechu

Member
Thanks to your suggestions and a little bit of awareness, I've quickly achieved an average FPS of 175, compared with earlier at 105. Which is awesome! And this is only by tweaking the major objects. Thanks guys!

About the draw_text thing. Is there a way to get around this massive drain? I tried to make a Paper Mario style textbox system with the ability to apply jitter and different effects to the individual letters. This meant each letter had to be a separate draw_text event. Sooo 200 characters = draw_text*200. You can imagine how that went.

What would you guys suggest for a work around in THAT kind of situation?
Only thing that comes in mind is to work with surfaces... Draw the whole text first on a surface, transform it and add effects... and than draw only this surface every frame...
 

YellowAfterlife

ᴏɴʟɪɴᴇ ᴍᴜʟᴛɪᴘʟᴀʏᴇʀ
Forum Staff
Moderator
Only thing that comes in mind is to work with surfaces... Draw the whole text first on a surface, transform it and add effects... and than draw only this surface every frame...
Vertex buffers are very well fit for this particular purpose.
 

Hyomoto

Member
The draw_text function is nothing else as looped draw_sprite function, if you look at the texture pages you can see that all fonts are converted into sprites...
So if you have a 20 character text to draw then it's equivalent to 20 draw_sprite function executions...
This may be true but are there any numbers that prove its actually equivalent? I'd be surprised if it's not optimized in any way.

My experience with this in the past is that a loop is more expensive than the draw calls themselves which is where I would expect draw_text to win.
 

Electros

Member
During development, I pop the instance count and FPS into the corner of the screen:

Code:
//Debugging values
draw_set_halign(fa_left);
draw_set_font(fnt_Main_Debug);
draw_text(20, 20, "Instance count: " + string(instance_number(all)));
draw_text(20, 40, "FPS: " + string(fps_real));
If I see any big unexpected drops in FPS, or ever growing instance counts, it can suggest a duff change or some things not being destroyed that should be!
 

Smiechu

Member
This may be true but are there any numbers that prove its actually equivalent? I'd be surprised if it's not optimized in any way.

My experience with this in the past is that a loop is more expensive than the draw calls themselves which is where I would expect draw_text to win.
It's only my rough estimation, I didn't say that it's 100% the same.
Loops are often "revolved" by the compilers, if there is no excesive logic inside, so i wouldn't say that loops self are a performance problem. Problem is the amount of iterated operations they generate with very small amount of code...
 

Hyomoto

Member
It's only my rough estimation, I didn't say that it's 100% the same.
Loops are often "revolved" by the compilers, if there is no excesive logic inside, so i wouldn't say that loops self are a performance problem. Problem is the amount of iterated operations they generate with very small amount of code...
My tests are hardly comprehensive, but in GM:S I was working on a tile engine and I found that the biggest problem was actually the iteration through a list to actually draw the items themselves as opposed to the draw calls. Basically I removed all logic and just had the draw calls. Still expensive. Removed all the draw calls and while it did make a difference, it showed the loop doing nothing was almost as expensive as having it draw the tiles to the screen. Which honestly surprised me. The only way to optimize was to actually reduce the size of the loops or how frequently they were called. In this case we are talking iterating over a few thousand tiles, but it was interesting to see that looping a few thousand times was more expensive than drawing a few thousand times. For it's part I think GM's draw calls are reasonably well optimized.

Then again, as I said, this is just based on my experience which is really why I was asking if there was any numbers to go with that. I'm always interested to hear about optimizations and 'best behaviors'.
 

NightFrost

Member
I once had the grand idea of reducing draw load by not drawing instances that were not in view... using a rectangle_in_rectangle check. The result was: nope. Doing the check for all of them and just drawing a few was actually slower than not checking anything and drawing everything. Draw calls, it seems, use relatively little time (breaking draw batches of course is a different matter).
 

Smiechu

Member
I once had the grand idea of reducing draw load by not drawing instances that were not in view... using a rectangle_in_rectangle check. The result was: nope. Doing the check for all of them and just drawing a few was actually slower than not checking anything and drawing everything. Draw calls, it seems, use relatively little time (breaking draw batches of course is a different matter).
If you have only draw functions in the draw event then this is true... becouse GM draw engine checks if it will draw in the range of active view... if not he skips the execution of draw functions...
But, if from any reason you have some heavy logic or any sort of other operations i.e. iteration through array... then switching drawing (skipping execution of whole draw event) should give a good effects...

All the things here are very, very individual... there is no one universal good recepie...
I also found out lately that sprite_create_from_surface is a very heavy function and a simple surface_copy is way faster if you can use surface instead of sprite...
 

dphsw

Member
My guiding principles are:

1. Avoid GML code in favour of built-in functions, even where it seems counterintuitive.

Since GameMaker itself isn't actually written in GML (I'm not sure what it actually is written in, I'm guessing C++) it's own functions will run far faster than anything you can write. If you need to sum up a 2D array of numbers routinely, make sure they're in a ds_grid so that you can use ds_grid_get_sum to add them all up. Your code and the native GameMaker function will of course both do the same thing - run a loop over it that adds each value to a total - but the native function will run many times faster, just because it isn't written in GML. (For this reason, it's not at all surprising that, as Hyomoto said, a loop doing nothing was as expensive as some draw calls - the loop doing nothing was written in GML, whereas the draw calls were native functions. If it were in C++, this wouldn't have been the case.)

2. Avoid writing functions in GML.

Something I've noticed GML is particularly bad at is calling one of its own functions. (By 'one of it's own functions', I mean a function written in GML, not one of the GameMaker functions.) I once had a game in which I had a function to check for level collision at a pixel specified by the argument. I found that most of the runtime was being used by this function - even when I commented out all of the code. Once I took the code, and just copied and pasted it to everywhere in the program where that function was required, the game ran several times faster. Again, not something that would happen in C++ - in most languages it's considered good practice to keep your code readable by making everything out of fairly small functions, and if something is too complicated to be done in a dozen lines or so, it should be broken up into several other functions. But in GML, use as few functions as possible while still keeping code understandable, and sometimes it may even be necessary to sacrifice code readability and maintainability for the massive speedup it can give to eliminate frequently-used functions.
 

JackTurbo

Member
Mines pretty simple, once every couple of weeks I spend some time with the profiler.

If anything is using more step time than I think it ought to I'll take a look and work out why.
 

RangerX

Member
In addition to all the good stuff said in this thread, I also pay attention and organise the code in a way that something that doesn't NEED to read every step is indeed read only went absolutely necessary.
This is done by carefully crafting your step events or any event that are occuring every step and using sensible conditions + short circuit evaluation turned on.

Also, when you're using "short circuit evaluations", in your conditions you should always put the functions last. This way, the engine might not even need to execute the function at all.
 
Last edited:

Luke Peña

Member
In addition to all the good stuff said in this thread, I also pay attention and organise the code in a way that something that doesn't NEED to read every step is indeed read only went absolutely necessary.
This is done by carefully crafting your step events or any event that are occuring every step and using sensible conditions + short circuit evaluation turned on.

Also, when you're using "short circuit evaluations", in your conditions you should always put the functions last. This way, the engine might not even need to execute the function at all.
I had something like that were I created a function that checked what block my objects was above and this ran every step. I realized that since each block was 4 pixels wide, I was running this 3 times more often than I needed to. Plus instead of looking for an object below, those blocks were already stored in an array and created below the map in order. So I could know which block I was above by simply checking the array where the selection was x/blockWidth. This saved me a BUNCH of processing power.
 
What drives me nuts is how I'll still get 200 - 1,000+ real_fps and yet the game will somehow dip to 59 fps. I've made quite a lot of debuggers to track the lowest and highest real_fps per second so I know I'm not missing a frame where it possibly dips to 59 fps.

As for optimization...
- not everything needs to go each and every frame.
- Check for memory leaks, so if you're done using a sprite you made via code or a particle effect or a surface... destroy it properly.
- Check for for / while / repeat loops that go on far too long.
- Somehow creating a lot of instances at once seems to burden the game as does activating and deactivating a lot of things at once.
- Don't update a surface if you don't have to. That goes for maps, lighting and so on. Once every 2 or 3 frames might be the maximum you'll need beyond the game screen.
 

Smiechu

Member
What drives me nuts is how I'll still get 200 - 1,000+ real_fps and yet the game will somehow dip to 59 fps. I've made quite a lot of debuggers to track the lowest and highest real_fps per second so I know I'm not missing a frame where it possibly dips to 59 fps.
This was already said here. You can do magic (hyper optimization) in the step events, but the devil always hides in the draw event...
 

Smiechu

Member
I suppose I should optimize my post by removing it.
Hey! Chill! :) It's a misunderstanding... I was referring to your problems with FPS. If the FPS drops down while FPS real is high, than you need to focus on optimizing the draw events... that I had in mind. Sorry, English is not my native language.

The second part of your post is OK and I'm 100% agreeing with it!
 
G

grixm

Guest
1. Avoid GML code in favour of built-in functions, even where it seems counterintuitive.

Since GameMaker itself isn't actually written in GML (I'm not sure what it actually is written in, I'm guessing C++) it's own functions will run far faster than anything you can write. If you need to sum up a 2D array of numbers routinely, make sure they're in a ds_grid so that you can use ds_grid_get_sum to add them all up. Your code and the native GameMaker function will of course both do the same thing - run a loop over it that adds each value to a total - but the native function will run many times faster, just because it isn't written in GML. (For this reason, it's not at all surprising that, as Hyomoto said, a loop doing nothing was as expensive as some draw calls - the loop doing nothing was written in GML, whereas the draw calls were native functions. If it were in C++, this wouldn't have been the case.)
This isn't always the case. I've found several functions in GM that perform much worse than raw GML. For example color_get_red(color) vs (color & $FF)
 
T

The Sentient

Guest
I use the 'keep it simple' rule.

Break down the task at hand. What is the simplest way to achieve it? (and I don't mean laziest, i.e. Use an inbuilt collision function and mark everything solid).
 
Top