Spritesheets vs Individual Sprites
#1 Members - Reputation: 158
Posted 09 July 2012 - 10:41 PM
The question that I am asking is what is better to do, use spritesheets or individual sprites, or what is a perfect balance between the two?
I am currently working on a game, and I want to make sure that it is effecient. I actually split up all the characters/npcs/etc, and then halfway through I realized how costly it is to do that. I am using DirectX9, and I believe even with OpenGL, using SetTexture is very costly. The pros of having individual tiles in their own image file is that you won't use as much memory because you only have loaded what you needed. It is very ineffecient because SetTexture is costly and it loses a lot of performance.
Using one big tileset is also a bad idea because some graphics cards have a texture maximum size and if you're tileset is big enough(which is very possible) then you're application will not be compatible with certain people's computers. It also uses a lot of memory to do this because you have all the tiles in memory but you are probably only using 5-10% of the actual loaded textures. A pro of this is that it will be effecient performance-wise because you only have to call SetTexture once and you just clip what you need.
I believe that to be the most effecient, I will need to create a perfect balance of the two. I am thinking that I want to make each texture a 512x512 size and once it gets to that size, then I will create a second texture to continue on. Most graphics cards will be able to support a 512x512 texture, it gives you a performance boost because the spritesheets will be split up by category, so I will be calling SetTexture once, render category, and move on. It is a bit of a memory waste, but not as much as the one big tileset would be. I would much prefer to have my application render faster than worry about memory as much. Most computers can handle the memory, and as long as it isn't ridiculous, then it will be fine.
What are your guys' thoughts on this? This is meant to be a conceptual topic, but it seems like it is leaning towards my problem. I just want to hear what you guys think and maybe that will help influence me to reach a conclusion about my problem. And so you guys know, tilesheet = spritesheet, tiles = sprites. I use them interchangeably for some reason.
#2 Members - Reputation: 400
Posted 10 July 2012 - 04:43 AM
In a nutshell: how effective spritesheets are over individual sprites depends on your game, my game is probably one of the strongest cases you'll find, if you're talking about 16 8x8 sprites, however, it probably won't make that much sense for example. How large you can make your spritesheets depends on the hardware you're targeting, you mention 512x512, that's extremely conservative. If you're not aiming at supporting really old GPUs you can certainly go up to 4096x4096, I'd be willing to bet that all GPUs that came out like 6 years or earlier maybe(?) will support this texture size. And then there's even more to it, if you look at memory consumption, you need to consider using DXTn texture compression. This will also speed up loading times in case you're converting each loaded texture from their PNG or JPEG format to RGB(A) at the moment. Last but not least, make sure to use a vertex shader for selecting the portion of the spritesheet you actually want. Doing this on the CPU is a really bad idea these days, for obvious reasons.
Here's an example of a vertex shader that does this (HLSL):
[source lang="cpp"]float4x4 mat_worldviewproj;float4 rect;struct vs_input{ float4 position : POSITION; float2 uv : TEXCOORD0;};struct vs_output{ float4 position : POSITION; float2 uv : TEXCOORD0;};vs_output MyVertexShader ( vs_input input ){ vs_output output; // transform vertex into screen space for rendering output.position = mul ( float4 ( input.position.xyz, 1.0f ), mat_worldviewproj ); // uv atlas (u/v/w/h of sprite in sheet, all range 0..1) output.uv.x = ( input.uv.x * rect.z ) + rect.x; output.uv.y = ( input.uv.y * rect.w ) + rect.y; return output;}[/source]
Just feed it the vector4 "rect" with the top left corner coordinates as the first two elements and then the width and height of the portion you want from the spritesheet (all in range 0..1 - this way the size of your texture is irrelevant to the shader).
Gone on a bit of a tangent there, hope this helps you in some way!
Edited by d k h, 10 July 2012 - 05:44 AM.
#4 Members - Reputation: 3827
Posted 10 July 2012 - 06:37 AM
That doesn't mean that reducing the number of SetTexture calls is something you can ignore - it still has benefit - but just that the primary advantage is going to be elsewhere.
If you're using ID3DXSprite you'll find that it automatically does the required draw call batching for you; if not you'll need to do a little extra work yourself (but it's not that difficult).
Regarding texture size, with SM2.0 hardware you can generally rely on at least 2048x2048 textures being available, although SM2.0 doesn't specify a texture size (I've seen it go down to 1024x1024 but never lower). If you're coding to SM3.0 you're guaranteed at least 4096x4096 - source: http://msdn.microsoft.com/en-us/library/bb219845%28v=VS.85%29.aspx
It appears that the gentleman thought C++ was extremely difficult and he was overjoyed that the machine was absorbing it; he understood that good C++ is difficult but the best C++ is well-nigh unintelligible.
#5 Members - Reputation: 158
Posted 10 July 2012 - 11:00 AM
Even if I were to use 4096x4096 or whatever texture size, I am wanting to answer the question "What if I exceed that size with my textures?" I am trying to think on a bigger scale(which in the game I'm making, I will certainly exceed 1024x1024 a few times). I could make another textures to hold this data but whenever I am in the rendering layer, I would most likely end up switching textures from the first page to the second page. As I slowly add on more and more "pages" of textures, the more I have to switch textures between them.
Any ideas?
#6 Members - Reputation: 376
Posted 10 July 2012 - 12:15 PM
Thanks d k h, that story is really comforting to know. It tells me that spritesheets are the way to go for sure. I am wanting to support older graphic cards, so I am trying to pick a relatively low texture size that a lot of graphic cards will support. From what mhagain says, I should be safe with 1024x1024, which sounds good.
Even if I were to use 4096x4096 or whatever texture size, I am wanting to answer the question "What if I exceed that size with my textures?" I am trying to think on a bigger scale(which in the game I'm making, I will certainly exceed 1024x1024 a few times). I could make another textures to hold this data but whenever I am in the rendering layer, I would most likely end up switching textures from the first page to the second page. As I slowly add on more and more "pages" of textures, the more I have to switch textures between them.
Any ideas?
As I stated before 3D textures are way to go, if you are concerned about switching textures.
If you feel any problem with 3D textures (yes they may not work on really old hardware) then your only way to go is to determine maximum texture size available on computer through caps, then dynamically create bigger texture sheet.
Say you have 4 textures with 1024x1024 size and maximum available texture size on computer is 4096x4096, then you during loading process putt all 4 textures on one bigger. Of course you will need to have special class for handling UV of dynamically created textures but that is another story.
But as mhagain stated probably you will not have too much problems with texture switching, biggest bottleneck you will probably have is a number of draw calls.
Just try to make 4000 BeginSprite/DrawSprite/EndSprite calls then make same with 1 BeginSprite, 4000 DrawSprite and 1 EndSprite call,l and you will understand the problem.
#7 Members - Reputation: 158
Posted 10 July 2012 - 12:55 PM
Thanks d k h, that story is really comforting to know. It tells me that spritesheets are the way to go for sure. I am wanting to support older graphic cards, so I am trying to pick a relatively low texture size that a lot of graphic cards will support. From what mhagain says, I should be safe with 1024x1024, which sounds good.
Even if I were to use 4096x4096 or whatever texture size, I am wanting to answer the question "What if I exceed that size with my textures?" I am trying to think on a bigger scale(which in the game I'm making, I will certainly exceed 1024x1024 a few times). I could make another textures to hold this data but whenever I am in the rendering layer, I would most likely end up switching textures from the first page to the second page. As I slowly add on more and more "pages" of textures, the more I have to switch textures between them.
Any ideas?
As I stated before 3D textures are way to go, if you are concerned about switching textures.
If you feel any problem with 3D textures (yes they may not work on really old hardware) then your only way to go is to determine maximum texture size available on computer through caps, then dynamically create bigger texture sheet.
Say you have 4 textures with 1024x1024 size and maximum available texture size on computer is 4096x4096, then you during loading process putt all 4 textures on one bigger. Of course you will need to have special class for handling UV of dynamically created textures but that is another story.
But as mhagain stated probably you will not have too much problems with texture switching, biggest bottleneck you will probably have is a number of draw calls.
Just try to make 4000 BeginSprite/DrawSprite/EndSprite calls then make same with 1 BeginSprite, 4000 DrawSprite and 1 EndSprite call,l and you will understand the problem.
I believe I am already using 3D textures? I am doing this in C# using SharpDX, and I only use the Direct3D9 class, so I assume it's a 3D texture. I also have a function to call BeginScene and one to call EndScene. So I call BeginScene, draw everything that is needed on the screen, and then call EndScene & Present to display it. The number of draw calls is inevitable because it doesn't matter if I use a spritesheet or not, I will have to call the draw function the same amount of times.(approximately, not enough to notice)
I think I will make another test program and get some data seeing how slow SetTexture is.
"I could make another textures to hold this data but whenever I am in the rendering layer, I would most likely end up switching textures from the first page to the second page. As I slowly add on more and more "pages" of textures, the more I have to switch textures between them."
I am still not sure what to do about this issue.
I actually didn't think of making a bunch of 1024x1024 spritesheets, and then combining them to the graphic cards max texture size. That will make it more efficient. I can also spit out an error message if they don't have at least a 1024x1024 max texture size.
#8 Members - Reputation: 376
Posted 10 July 2012 - 02:14 PM
#9 Members - Reputation: 158
Posted 10 July 2012 - 02:26 PM
#10 Members - Reputation: 376
Posted 10 July 2012 - 11:44 PM
However if I remember well, I have read somewhere that 3th dimension on texture also should be power of 2. So it is wise to have 1,2,4,8,16 etc layers.






