GML [SOLVED] Sprite Sheets vs Individual Sprites

Carloskhard

Member
So Porgrammers like Friendly Cosmonaut in this video:
... use sprite sheets, 'draw_sprite_part' and a lot of code to draw different objects instead of using individual frames.

So my question is clear: What is the advantage of doing this in this situation? (Considering is harder to program)
I'm also programming my inventory system but I can't see how this way of doing it with sprite sheets is better if you need much more code to do the same. Is it more efficient at CPU level maybe? And if so, is the difference enough to be worth?

Thanks!
 
Last edited:
I'm not the most informed person, but this might have some merit (it could also be totally wrong)

It's possible that, to avoid having to call multiple texture pages for various items, they are instead putting them all into one sprite.

So when you want to draw particular elements you don't:
(a) need to use objects when showing the inventory (which would otherwise have their own sprites, draw calls, and other function costs)
(b) it limits things overall - like the need to look up multiple texture pages, or flushing the graphics.

Whilst it may seem that more code is bad - it really depends on how low level that code is, versus the cost of doing unnecessary engine functions that the lack of extra code incurs.

They could, for example, be drawing that inventory on to a surface, and instead of the inventory being redrawn every step they just hold the surface (which is a full snapshot of it). Unless the content of the inventory changes there is no point changing what is drawn, so you could hold that surface in memory for as long as it is relevant. The cost then is of holding the surface in memory and drawing it, rather than doing multiple draw calls per step which are a cpu / gpu cost.

Maybe....:)
 

Carloskhard

Member
I'm not the most informed person, but this might have some merit (it could also be totally wrong)

It's possible that, to avoid having to call multiple texture pages for various items, they are instead putting them all into one sprite.

So when you want to draw particular elements you don't:
(a) need to use objects when showing the inventory (which would otherwise have their own sprites, draw calls, and other function costs)
(b) it limits things overall - like the need to look up multiple texture pages, or flushing the graphics.

Whilst it may seem that more code is bad - it really depends on how low level that code is, versus the cost of doing unnecessary engine functions that the lack of extra code incurs.

They could, for example, be drawing that inventory on to a surface, and instead of the inventory being redrawn every step they just hold the surface (which is a full snapshot of it). Unless the content of the inventory changes there is no point changing what is drawn, so you could hold that surface in memory for as long as it is relevant. The cost then is of holding the surface in memory and drawing it, rather than doing multiple draw calls per step which are a cpu / gpu cost.

Maybe....:)
Thanks for the reply!

I don't know much efficient could it be, but I know you could reorgince texture pages so all the items are in one, and also you don't need to use multiple objects to draw different sprites.Not even different sprites, just put all the items in one sprite. So those two points are not very different,I think.

Also the draw calls are the same whether you use one system or another.In fact the draw calls from the method of the video is more cost efficient since you have to cut a certain part of an image every frame.
So as you can see I'm not that sure about some of what you said. Correct me if I'm wrong!
 
Wellllll....I can't correct you if you're wrong, because I don't know that for sure. I did also say that my input could be wrong.

The number of draw calls will be the same if it used instances with their own sprites, but then you'd have to reference the instances. That could be an extra bit of cost, or hassle, orchestrating it across multiple objects, compared to one object handling it all.

For example: Say that it is correct that they could have "snapshotted" the inventory, and use a surface to show the whole of it. That would be the initial cost of drawing it all, and then after that (probably) a much reduced cost of just drawing the surface. If the inventory doesn't change in a whole second, then why make the extra draw calls per step, for every step, when you could "scoop" them up into one graphic, hold it in memory, and display that instead? One object and one draw call, versus several instances each with their own draw calls.

Also, unlike sprites, I believe a surface is not held like a texture page, so maybe doesn't have an engine cost like flushing the graphics would have? It's not quite my level of capability to say with 100% certainty.

Instead of using instances, and doing some collision events to see if the mouse is colliding with a particular object of the inventory, and then having to get that object to communicate back some things, they just went on the mouse position and figured out the rest. By defining beforehand how they set up the UI / inventory etc they could be reducing unnecessary things, simply by sticking to a framework where they know its proportions.

instance_example.png

If you did the above, and used instances, it might go like this:
1) Setting all 12 to be visible / invisible when wanting to display, or turn off, the inventory
2) 12 draw calls when they are visible, per step
3) 12 instances to be checked if you use some form of collision for finding out which one currently has a mouse on it
4) If you went with point 3 it maybe takes referring back some info to the inventory object as to what was clicked on, and then another piece of data from the inventory object back to the "clicked" instance as to what it should do.

Take the same set up and do it through one drawing (a surface) or one object drawing it:
1) One instance being set to invisible / visible when wanting the inventory to be displayed or not
2) 12 draw calls for, say, one step, and then one draw call after that if using a surface to show everything.
3) No collision checking, at all. Because you know where it is displayed on the screen, what each block will be, and what the dimensions are, you instead use the mouse coordinates.

if mouse_x div block_width == 5
{if mouse_y div block_height == 10
{is_a_carrot = true;
do whatever}
if mouse_y div block_height == 20
{is_a_cauliflower = true;
do whatever}
if mouse_y div block_height == 30
{is_a_carrot = true;
do whatever}
}
You wouldn't have to interact with any other instances, and I think a purely mathematical function is faster than any collision checking to achieve the same result. It would take some thought setting it up, and be a little rigid in it's execution, but ultimately takes off some the strain and performance cost.

Without that developer explicitly stating why they've done certain things you can only guess at the intention. If my best guesses are wrong....hopefully someone more knowledgeable than me will answer this thread.
 
Last edited:

Carloskhard

Member
Wellllll....I can't correct you if you're wrong, because I don't know that for sure. I did also say that my input could be wrong.

The number of draw calls will be the same if it used instances with their own sprites, but then you'd have to reference the instances. That could be an extra bit of cost, or hassle, orchestrating it across multiple objects, compared to one object handling it all.

For example: Say that it is correct that they could have "snapshotted" the inventory, and use a surface to show the whole of it. That would be the initial cost of drawing it all, and then after that (probably) a much reduced cost of just drawing the surface. If the inventory doesn't change in a whole second, then why make the extra draw calls per step, for every step, when you could "scoop" them up into one graphic, hold it in memory, and display that instead? One object and one draw call, versus several instances each with their own draw calls.

Also, unlike sprites, I believe a surface is not held like a texture page, so maybe doesn't have an engine cost like flushing the graphics would have? It's not quite my level of capability to say with 100% certainty.

Instead of using instances, and doing some collision events to see if the mouse is colliding with a particular object of the inventory, and then having to get that object to communicate back some things, they just went on the mouse position and figured out the rest. By defining beforehand how they set up the UI / inventory etc they could be reducing unnecessary things, simply by sticking to a framework where they know its proportions.

Without that developer explicitly stating why they've done certain things you can only guess at the intention. If my best guesses are wrong....hopefully someone more knowledgeable than me will answer this thread.
I meant one object drawing all the sprites in the inventory in both cases, just with different techniques.
Also in the tutorial there isn't any screenshot or surfaces involved, just using parts of a big sprite instead of frames in one sprite, so that part it isn't involved in this problem, I think you misunderstood that :c.

To sum up, the diference is between drawing frames of a sprite(which is easy to acces and program) or drawing parts of a big unique frame in a sprite and then managing all the program to draw just that part. You can have a little watch to the video I linked.
 
I know they didn't use a surface in that tutorial. It was included as an example of how you can sometimes cut costs, depending on the type of game you're making.

I edited my previous post to try and show how some small things add up cost wise. But I will leave it at that, as I am only guessing here.

EDIT:
One other thing about draw calls is that I'm not sure how they work. If you call up a large sprite, and then draw sections of it, it may only count for one cost of finding the sprite details. The subsequent drawing of sections of it may cost less, than if you made several calls where a new sprite has to be accessed each time (even without a texture swap)

To draw 12 items seperately:
draw_sprite = find sprite index cost / find texture page cost / cost of drawing sprite (3 cost)
three engine processes multiplied by 12 = 36

To draw one sprite, 12 times:
var is_sprite = find sprite index cost / find texture page cost (2 cost) // in the video they do say about defining the sprite as a local variable (even though its a stored constant) so as to remove the cost of calling it within the step
draw_sprite_pos = cost of drawing part of sprite, times by 12 (12 cost)
2 engine processes, plus 12 more = 14

Even if the sprites are on the same texture page, when drawn separately you have the search for its sprite index and the texture page index search. There'd be no need for a texture swap, but it wouldn't matter, as any gain is lost through the need for repeated index searches. At least, I think that is the case.....

That kind of thing though is where you need a fairly advanced user answering it...which is not me. Good luck :)
 
Last edited:

Carloskhard

Member
I know they didn't use a surface in that tutorial. It was included as an example of how you can sometimes cut costs, depending on the type of game you're making.

I edited my previous post to try and show how some small things add up cost wise. But I will leave it at that, as I am only guessing here.

EDIT:
One other thing about draw calls is that I'm not sure how they work. If you call up a large sprite, and then draw sections of it, it may only count for one cost of finding the sprite details. The subsequent drawing of sections of it may cost less, than if you made several calls where a new sprite has to be accessed each time (even without a texture swap)

To draw 12 items seperately:
draw_sprite = find sprite index cost / find texture page cost / cost of drawing sprite (3 cost)
three engine processes multiplied by 12 = 36

To draw one sprite, 12 times:
var is_sprite = find sprite index cost / find texture page cost (2 cost) // in the video they do say about defining the sprite as a local variable (even though its a stored constant) so as to remove the cost of calling it within the step
draw_sprite_pos = cost of drawing part of sprite, times by 12 (12 cost)
2 engine processes, plus 12 more = 14

Even if the sprites are on the same texture page, when drawn separately you have the search for its sprite index and the texture page index search. There'd be no need for a texture swap, but it wouldn't matter, as any gain is lost through the need for repeated index searches. At least, I think that is the case.....

That kind of thing though is where you need a fairly advanced user answering it...which is not me. Good luck :)
Thanks for the detailed info and opinion.Very interesting! <3
 
There's even an odd quirk (?) in GMS, where defining permanent variables / constants / seemingly just about anything as a local variable when called in an event, is faster due to how the engine operates under the hood. I can't say that I understand the technical side of it, but having practiced doing this I've seen a 2 to 3 increase in my projects speed. Whether or not these developers were aware of it when coding the tutorial above it's a very useful thing to be aware of.

https://forum.yoyogames.com/index.php?threads/optimizing-code-for-yyc.62937/#post-380280
 

FrostyCat

Redemption Seeker
So Porgrammers like Friendly Cosmonaut in this video: ... use sprite sheets, 'draw_sprite_part' and a lot of code to draw different objects instead of using individual frames.

So my question is clear: What is the advantage of doing this in this situation? (Considering is harder to program)
I'm also programming my inventory system but I can't see how this way of doing it with sprite sheets is better if you need much more code to do the same. Is it more efficient at CPU level maybe? And if so, is the difference enough to be worth?
The issue with individual frames is the risk of the texture packer putting some frames into one sheet and the rest in another, thus incurring extra texture swaps that degrade performance in the long run. Having it all in one sheet ensures that the texture packer won't do that. It's a GPU-side optimization, not a CPU-side optimization.

An alternative is to manually organize the sprites using texture groups. Sprites, tilesets and fonts in the same texture group are grouped together when building texture sheets, so if there's a way for them to all fit on the same sheet, they will. Doing it this way removes the extra CPU-side overhead in manual frame selection and also retains the ease of use of individual frames. You just have to preview often to make sure they're all in one sheet.

Personally I disagree with FriendlyCosmonaut's recommendation. Yes, forcing the sheet to be always in one piece is a fine thing to do, but it also forces the group of sprites to be arranged in a fixed rectangular region. It will rule out other more space-efficient but irregularly shaped packing schemes that the texture packer may come up with in absence of this constraint. And as you said, her recommendation is a manual procedure while texture groups are built into the engine, so it is a valid concern in terms of practical convenience and the risk of undoing GPU-side performance gains with CPU-side overhead by implementing the GML improperly.

In any case, if you do implement FriendlyCosmonaut's optimization, make sure you abstract the details behind a script. That makes your work on this front a one-time expense. If you implement the optimization in a way that you're still manually calculating on-sheet coordinates at drawing time, you are doing it wrong.
 

Carloskhard

Member
The issue with individual frames is the risk of the texture packer putting some frames into one sheet and the rest in another, thus incurring extra texture swaps that degrade performance in the long run. Having it all in one sheet ensures that the texture packer won't do that. It's a GPU-side optimization, not a CPU-side optimization.

An alternative is to manually organize the sprites using texture groups. Sprites, tilesets and fonts in the same texture group are grouped together when building texture sheets, so if there's a way for them to all fit on the same sheet, they will. Doing it this way removes the extra CPU-side overhead in manual frame selection and also retains the ease of use of individual frames. You just have to preview often to make sure they're all in one sheet.

Personally I disagree with FriendlyCosmonaut's recommendation. Yes, forcing the sheet to be always in one piece is a fine thing to do, but it also forces the group of sprites to be arranged in a fixed rectangular region. It will rule out other more space-efficient but irregularly shaped packing schemes that the texture packer may come up with in absence of this constraint. And as you said, her recommendation is a manual procedure while texture groups are built into the engine, so it is a valid concern in terms of practical convenience and the risk of undoing GPU-side performance gains with CPU-side overhead by implementing the GML improperly.

In any case, if you do implement FriendlyCosmonaut's optimization, make sure you abstract the details behind a script. That makes your work on this front a one-time expense. If you implement the optimization in a way that you're still manually calculating on-sheet coordinates at drawing time, you are doing it wrong.
I'm super happy with your answer and the link @the_dude_abides brought. Very nice information to have.The help in this forum is amazing because of people like you :)
I'll rather organize the texture pages since is much more easy and I don't problems with that.
I'll mark this thread as solved now.
 

Masbeatz

Member
So Porgrammers like Friendly Cosmonaut in this video:
... use sprite sheets, 'draw_sprite_part' and a lot of code to draw different objects instead of using individual frames.

So my question is clear: What is the advantage of doing this in this situation? (Considering is harder to program)
I'm also programming my inventory system but I can't see how this way of doing it with sprite sheets is better if you need much more code to do the same. Is it more efficient at CPU level maybe? And if so, is the difference enough to be worth?

Thanks!
Hi im really new to Gms2 but i work since quiet 20years with photoshop. For me the advantages of a spritesheet as a designer is that you can edit all sprites at once. For example: you can change color or add outlines...
The only problem is that Game maker makes individual sprites out of all the spritesheets and these are then no longer connected to the spritesheet. This forces me to always re-import everything, which makes working with Gms2 much more tedious than with Unity. At least as far as that is concerned... Has anyone found a good workflow to use Photoshop and Gms2 together without always having to re-import everything?
 

TheouAegis

Member
Are you editing the sprite data inside Game Maker's project directory (where the YYP file is located), or are you editing the images in a folder elsewhere (e.g., on the Desktop)?
 
Top