Sign in to follow this  
sqpat

hardware, vram, texture questions

Recommended Posts

I'm trying to figure out how I should best store/load some massive sprite sheets that I will load as textures in my code. So I was wondering about a few things: Is there a problem with big textures? If so how big? I'm seeing some older info mostly talking 4096x4096... is 512x8192 good, or is squareness or some certain dimension cap so important? (I'd really like to cut down on the number of files I have to load for simplicity) And if I load from a 256 color png file is directX's createTextureFromFileEx smart enough to save this 1 byte per pixel in vram or does it convert to 24 bit? I notice theres a palette argument but I have no idea how this one works. Also, do things like vertexbuffers and indexbuffers get stored in vram? Do various hardware functions like the zbuffer or alpha blending or vertex fog tend to use vram? Trying very much to plan ahead here, so I'd be very happy to get any help i can here.

Share this post


Link to post
Share on other sites
Many DX8/DX9 ATI cards are limited to 2K textures, while nVidia has allowed 4K since the GeForce3. The problem being that they eat up ram quickly. Textures should be a power of 2 on each side (1,2,4,8,16,32,64, etc.), but don't need to be square. Some cards will allow arbitrary use of any size, but incur a speed hit. Just about all cards allow for aritrary sizes when certain limitations are met (ie you don't scale, use wrap texture addressing, etc. Search for nonpow2conditional in the d3dcap9 page.

You can save some texture space for 2D tiles by not using mipmaps (ask for 1 level, 0 means all) when you load your textures. These are used by the GPU when drawing textures smaller, and typically 2D games draw sprites at their original size.

Vertices and indices take up space in VRAM. The GPU's command buffer does to. The front buffer, backbuffer, and Z buffer (if you're using one) too.

While D3D9 has a palette based P8 format, it's optional, and you may as well pretend it isn't there. When running DX8 my GPU has P8 formats available. In DX9, the driver doesn't expose them, instead offering L8, and A8L8 formats that it didn't offer in DX8.

You can mimic a palette using ps.1.1 assembly. You first sample your greyscale palette index texture, then use texreg2ar, or texreg2gb to lookup a new color from your palette texture.

Two approaches could work.

For both, you would ask D3DX to load your paletted textures as P8 in pool_scratch (scratch ignores what your GPU can/cannot do), and provide an array of 256 palette entries to the load call.

For method 1, you'd create an L8 texture and copy the P8 data into it, then create an A8R8G8B8 1x256 texture. Copy the palette entries into it. Now, instead of setting 1 texture, you'd set both, have a ps.1.1 shader to tex t0, then texreg2ar t1, t0

For method 2, Each time you load a texture, increment a "palettenumber", starting from 0. For each image, create an A8L8 texture, lock it, and copy the P8 data to it. Copy the actual P8 value into the L8 bits, and fill the entire set of A8 bits with the "palettenumber". This gives you the palette entry, and a palette number on each pixel. When all your textures are loaded, create an N x 256 texture to hold all the palette data. You'd sample it the same way as method 1.

Method 1 requires 2 textures for each texture you want to render. Method 2 requires 1 texture for each, plus a single texture to hold all the palettes. Method 1 needs only 8 bits/pixel(L8), while method 2 needs 16(A8L8). The trade off is mainly the cost of "SetTexture" calls vs. memory usage.

Share this post


Link to post
Share on other sites
Quote:
Original post by Namethatnobodyelsetook
Many DX8/DX9 ATI cards are limited to 2K textures, while nVidia has allowed 4K since the GeForce3. The problem being that they eat up ram quickly. Textures should be a power of 2 on each side (1,2,4,8,16,32,64, etc.), but don't need to be square. Some cards will allow arbitrary use of any size, but incur a speed hit. Just about all cards allow for aritrary sizes when certain limitations are met (ie you don't scale, use wrap texture addressing, etc. Search for nonpow2conditional in the d3dcap9 page.


okay, so i think i will go with 2048x2048 instead of 512x8096, since it gives me the same area. I'll just do some different math there to look up the different sprites and it should be best this way.

Quote:
Original post by NamethatnobodyelsetookYou can save some texture space for 2D tiles by not using mipmaps (ask for 1 level, 0 means all) when you load your textures. These are used by the GPU when drawing textures smaller, and typically 2D games draw sprites at their original size.


all the mipmaps I use are stored on disk actually. these big ones wont use them at all so i'm not too worried there..

Quote:
Original post by Namethatnobodyelsetook
While D3D9 has a palette based P8 format, it's optional, and you may as well pretend it isn't there.

When running DX8 my GPU has P8 formats available. In DX9, the driver doesn't expose them, instead offering L8, and A8L8 formats that it didn't offer in DX8.

You can mimic a palette using ps.1.1 assembly. You first sample your greyscale palette index texture, then use texreg2ar, or texreg2gb to lookup a new color from your palette texture.

Two approaches could work.

For both, you would ask D3DX to load your paletted textures as P8 in pool_scratch (scratch ignores what your GPU can/cannot do), and provide an array of 256 palette entries to the load call.

For method 1, you'd create an L8 texture and copy the P8 data into it, then create an A8R8G8B8 1x256 texture. Copy the palette entries into it. Now, instead of setting 1 texture, you'd set both, have a ps.1.1 shader to tex t0, then texreg2ar t1, t0

For method 2, Each time you load a texture, increment a "palettenumber", starting from 0. For each image, create an A8L8 texture, lock it, and copy the P8 data to it. Copy the actual P8 value into the L8 bits, and fill the entire set of A8 bits with the "palettenumber". This gives you the palette entry, and a palette number on each pixel. When all your textures are loaded, create an N x 256 texture to hold all the palette data. You'd sample it the same way as method 1.

Method 1 requires 2 textures for each texture you want to render. Method 2 requires 1 texture for each, plus a single texture to hold all the palettes. Method 1 needs only 8 bits/pixel(L8), while method 2 needs 16(A8L8). The trade off is mainly the cost of "SetTexture" calls vs. memory usage.


ugh... so basically... support is being dropped for p8 and I shouldn't do it. Crap, i guess I'll have to use 24 bit. that might push reqs to over 128 MB of VRAM for what I want to do.

Thanks very much for the help though. I think I have a better idea of what I need to do now.

QUICK EDIT: Whoops, maybe i misunderstood. Are you saying there's a format where i can use a kind of pixelshader to get the memory usage of P8 (or a 16 bpp format)? If so that would be great. I will look into this.

Share this post


Link to post
Share on other sites
Quote:
Original post by Namethatnobodyelsetook
Method 2 requires 1 texture for each, plus a single texture to hold all the palettes. Method 1 needs only 8 bits/pixel(L8), while method 2 needs 16(A8L8).


Not sure what type of game you are making. However, there is another advantage to this approach that may be ideal for your game. If you create the pallete to be N x 256 in size, you can quickly draw the same object multiple times with different color schemes. For instance, you may have three enemy's that look identical, however each one has a different colored shirt (Red, Green, Blue). You can draw all three enemy's without needing to change texture states in between each draw call. Instead, you would simply update an index value (exposed via some uniform shader value) to sample the corresponding pallete. Just some food for thought :-)

Share this post


Link to post
Share on other sites
Honestly? I've decided I'll just 32 bit argb or xrgb everything. I should still sit under a 128 MB RAM requirement which is fine, and I'll just use alpha blending instead of palette swapping as the last poster mentioned to change little things like that.

BTW, what I am doing is making an full motion battle system for an rpg:

http://sqpat.com/project/mysample30twocharacters.PNG

Because the sprite "turns" relative to camera direction, you need 64x512 pixels just to represent a single frame. So when you factor in animations, and four characters at a time, having all your graphics in VRAM starts to add up, especially with 32 bits per pixel. I'm shooting for 128 frames of animation (times the 8 directions) per character which comes out to 16 MB of VRAM per, and thus 64 MB of VRAM just for the four characters. Throw in other textures, and animations, enemy graphics, buffers, etc... i was getting unsure about how much one would need to run this.

Share this post


Link to post
Share on other sites
Quote:
Original post by sqpat
okay, so i think i will go with 2048x2048 instead of 512x8096, since it gives me the same area. I'll just do some different math there to look up the different sprites and it should be best this way.
Really, don't do that. Just because most cards support 2048x2048, doesn't mean all do. You should really check the maximum texture size available from D3DCAPS9 and adjust your code accordingly - even if it's a rather brutal error of "Sorry, your graphics card does not support 2048x2048 textures".

Quote:
Original post by sqpat
all the mipmaps I use are stored on disk actually. these big ones wont use them at all so i'm not too worried there..
If you're using PNG for images, then you don't have multiple mipmaps stored in one file (You only do with DDS files). When you call D3DXCreateTextureFromFileEx, you tell it how many mip levels you want it to auto-generate. The default is 0 (Which is the value that's assumed if you use D3DXCreateTextureFromFile(non-Ex)), which means "Generate a full mipmap chain down to 1x1". You'll want to make sure you tell the function to only generate the top mip level, which is the original image.

Quote:
Original post by sqpat
ugh... so basically... support is being dropped for p8 and I shouldn't do it. Crap, i guess I'll have to use 24 bit. that might push reqs to over 128 MB of VRAM for what I want to do.

Thanks very much for the help though. I think I have a better idea of what I need to do now.

QUICK EDIT: Whoops, maybe i misunderstood. Are you saying there's a format where i can use a kind of pixelshader to get the memory usage of P8 (or a 16 bpp format)? If so that would be great. I will look into this.
D3DFMT_P8 has very little support; so little that you can pretend it's not there (Only a couple of old cards actually support it). However, you can get the memory usage of 8-bits per pixel using D3DFMT_L8 and a pixel shader, which has much more support, but means you need to use pixel shaders, which some older cards don't support (I'm not sure if the GeForce 4 supported pixel shaders, I know the GeForce 2/3 didn't).

Share this post


Link to post
Share on other sites
I suggest you forget about using palette indexed textures and use DXT compression instead. Rendering with palette textures are slower than DXT textures and the hardware can no longer do bilinear filtering for you.

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
Really, don't do that. Just because most cards support 2048x2048, doesn't mean all do. You should really check the maximum texture size available from D3DCAPS9 and adjust your code accordingly - even if it's a rather brutal error of "Sorry, your graphics card does not support 2048x2048 textures".


Err... I don't want to spend too long handling tons of these cases... I really want to go for something "safe" as far as these spritesheets go because they'll be all over the place; I don't want to rewrite too much code here to handle a bunch of hardware support cases. Perhaps I'll go with sixteen 512x512s instead of a 2048x2048... and if your card cant handle that, maybe it doesnt have the 128 MB vram requirement either in the first place. I mean, I know I'll be checking for some other stuff like vertex fog or certain texture filters, etc... i'll have certain performance options... i just dont want textures to have to be one of those. Does 512x512 ARGB sound 'safe' enough?

Quote:
Original post by Evil Steve
If you're using PNG for images, then you don't have multiple mipmaps stored in one file (You only do with DDS files). When you call D3DXCreateTextureFromFileEx, you tell it how many mip levels you want it to auto-generate. The default is 0 (Which is the value that's assumed if you use D3DXCreateTextureFromFile(non-Ex)), which means "Generate a full mipmap chain down to 1x1". You'll want to make sure you tell the function to only generate the top mip level, which is the original image.


I used irfanview (an editing program) to batch resize all my original textures to 50% like five times and i load each one and link them manually in the program, actually.

Quote:
Original post by Evil Steve
D3DFMT_P8 has very little support; so little that you can pretend it's not there (Only a couple of old cards actually support it). However, you can get the memory usage of 8-bits per pixel using D3DFMT_L8 and a pixel shader, which has much more support, but means you need to use pixel shaders, which some older cards don't support (I'm not sure if the GeForce 4 supported pixel shaders, I know the GeForce 2/3 didn't).


yeah it seems like argb (32 bit) is the safest/fastest bet for the general hardware im going for.

Quote:
Original post by DonnieDarko
I suggest you forget about using palette indexed textures and use DXT compression instead. Rendering with palette textures are slower than DXT textures and the hardware can no longer do bilinear filtering for you.


I'm actually not using bilinear filtering for the most part, haha... it causes trouble with sprites in worldspace because you can't really offset the texture coordinates and the filtering averages border pixels with transparent pixels.

Share this post


Link to post
Share on other sites
Quote:
Original post by sqpat
Err... I don't want to spend too long handling tons of these cases... I really want to go for something "safe" as far as these spritesheets go because they'll be all over the place; I don't want to rewrite too much code here to handle a bunch of hardware support cases. Perhaps I'll go with sixteen 512x512s instead of a 2048x2048... and if your card cant handle that, maybe it doesnt have the 128 MB vram requirement either in the first place. I mean, I know I'll be checking for some other stuff like vertex fog or certain texture filters, etc... i'll have certain performance options... i just dont want textures to have to be one of those. Does 512x512 ARGB sound 'safe' enough?
Well, it all depends what level of hardware you want to support. Technically, the minimum requirement is 256x256 (I'm not 100% sure on that though, anyone care to confirm / deny?), but pretty much every recent card will support 512x512 or 1024x1024 textures. So long as you check the available texture size at startup (Via the device caps) and bail out if it's too low, it doesn't really matter too much what you do.

Quote:
Original post by sqpat
I used irfanview (an editing program) to batch resize all my original textures to 50% like five times and i load each one and link them manually in the program, actually.
I see. You'll still need to make sure to use D3DXCreateTextureFromFileEx rather than D3DXCreateTextureFromFile and specify 1 for the number of mip levels. Otherwise D3D will auto-generate the lower mip levels by doing exactly what you've done in Irfanview.

Quote:
Original post by sqpat
I'm actually not using bilinear filtering for the most part, haha... it causes trouble with sprites in worldspace because you can't really offset the texture coordinates and the filtering averages border pixels with transparent pixels.
In any case, it may be worthwhile looking into DXT formats, since they're compressed in VRAM so will require less VRAM overall.

Share this post


Link to post
Share on other sites
hm. looked at DXT compression. interesting, but wont be too helpful for me; i pretty much am using all this space on sprites that i need good precision with, i was hoping for something lossless. Especially since i am using nearest point texture filtering. but its something i'll keep in mind for the future, thanks.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this