Sign in to follow this  
evolutional

2D Game Background (non-repeating)

Recommended Posts

Hey there, For my 4E4 entry I'm needing a large non-repeating background for my game. I'm wondering the best way to do this and maintain a decent speed. At present, I've loaded the whole background as a texture and rendered a textured quad behind the other spites (also other textured quads). Obviously, this is far from ideal so I'd like to call on your expertise in helping me solve this. Using the textured quad limits me to ^2 background sizes, something I'm not too keen on having. My game is low resolution (640,480) and so a 'small' background would be 1280x480 - creating a ^2 version of this texture would be a nightmare... One idea I had was to 'panel' the background. Take the source image and split it into smaller panels which are then rendered according to the appropriate view. A panel here would probably be tall and thin (640 or the next ^2 up x 256), but again it seems like it would be slow. An idea I had last night would be to use glDrawPixels to draw the bitmap striaght into the framebuffer. I assume I could specify which part to draw from, but I don't know how fast this will be... My graphics card is low-end (GF2 MX) with a fairly decent CPU (AMD 2800+), so the solution needs to be able to work on this setup. Anyone that's able to help will receive a nice rating bonus plus a credit in my 4E4 game entry as thanks. Thanks for your time!

Share this post


Link to post
Share on other sites
First of all, textures need not be powers of two if you use one of the rectangular texture extensions: GL_ARB_texture_rectangle, GL_EXT_texture_rectangle or GL_NV_texture_rectangle. However, if 1280x480 is 'small', then you'll probably run out of texture memory pretty fast anyway. On top of that, the maximum width of a texture is only 4096 on my geforce3, and it certainly won't be better on your geforce2. This means tiling is probably your only option if you go for the texture solution.

glDrawPixels will probably be (somewhat) faster then the texture option from a rendering point of view. However, for your application, the function-call has a rather ackward specification:

void glDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);

Which means that each frame you will have to construct a new pixel-set of size width x height from your full buffer, which definately won't be cheap.

I would go for a tiled texture implementation as I believe the copy for glDrawPixels will kill any performance gains it has over using textures. The only way to know for sure is to try both, but I would go for the tiled texture solution first.

Tom

Share this post


Link to post
Share on other sites
Quote:
Original post by evolutional
Hey there,

One idea I had was to 'panel' the background. Take the source image and split it into smaller panels which are then rendered according to the appropriate view. A panel here would probably be tall and thin (640 or the next ^2 up x 256), but again it seems like it would be slow.

Thanks for your time!


If this is what you plan you should look into glAddSwapHintRectWIN (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/opengl/glfunc01_16zy.asp).
This will allow you to rectangular areas to be copied on calling SwapBuffers. However this is for windows only. The advantage of this method is - this should speed up things significantly even if you run in software mode. With glDrawPixels I don't think that will happen.

Hope this helps.

Share this post


Link to post
Share on other sites
Quote:
Original post by dimebolt
First of all, textures need not be powers of two if you use one of the rectangular texture extensions: GL_ARB_texture_rectangle, GL_EXT_texture_rectangle or GL_NV_texture_rectangle.


I'm hoping that my crappy card supports that extension ;)

Quote:

glDrawPixels will probably be (somewhat) faster then the texture option from a rendering point of view. However, for your application, the function-call has a rather ackward specification:

void glDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);

Which means that each frame you will have to construct a new pixel-set of size width x height from your full buffer, which definately won't be cheap.


Could I not just store the image data in memory and alter the pointer to hte pixels instead of copying from one buffer to another? My data is kept in a form that's ready to be used by the OpenGL texturing functions, I'm hoping that I can set up glDrawPixels to read that buffer without a need for transformation. I'll check it out later.
Quote:

I would go for a tiled texture implementation as I believe the copy for glDrawPixels will kill any performance gains it has over using textures. The only way to know for sure is to try both, but I would go for the tiled texture solution first.


From what you've said I think I will give the tiled option a go first. I use a handle-based texture management system in my game, so I *should* be able to wire it up to cache the tile before it's displayed and uncache it when it goes of screen. This would be a good way of managing the backgound, essentially allowing me to have huge backgrounds without needing to keep the whole image in memory.

I'll give this a try tonight - thanks for your help :)

CRACK123: Thanks, I'll check out that function. I *did* want the game to be cross-platform though, but it could be a useful fallback if the method I'm planning doesn't work out too well.

Share this post


Link to post
Share on other sites
Quote:
Original post by evolutional
Could I not just store the image data in memory and alter the pointer to hte pixels instead of copying from one buffer to another? My data is kept in a form that's ready to be used by the OpenGL texturing functions, I'm hoping that I can set up glDrawPixels to read that buffer without a need for transformation. I'll check it out later.


Good point. You could call glDrawPixels(1, height, ...) width times (or the other way around, depending on the way your buffer is organized) with the proper pointers into your main buffer and using glRasterPos(...). Didn't think of that in the absence of my morning coffee. But still, I doubt it will be much faster than the texture solution. It might be interesting to try both though.

Tom

[Edited by - dimebolt on June 29, 2005 6:05:12 AM]

Share this post


Link to post
Share on other sites
Quote:

Could I not just store the image data in memory and alter the pointer to hte pixels instead of copying from one buffer to another? My data is kept in a form that's ready to be used by the OpenGL texturing functions, I'm hoping that I can set up glDrawPixels to read that buffer without a need for transformation. I'll check it out later.


How about using compressed textures and using glTexCopySubImage* or glTexCopyImage* to modify parts of the background ? This could be a much more fasible solution than glDrawPixels. Plus this way you will only be changing the pixel information but using the same textures.

Hope this helps.



Share this post


Link to post
Share on other sites
Quote:
Original post by evolutional
Quote:

glDrawPixels will probably be (somewhat) faster then the texture option from a rendering point of view. However, for your application, the function-call has a rather ackward specification:

void glDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);

Which means that each frame you will have to construct a new pixel-set of size width x height from your full buffer, which definately won't be cheap.


Could I not just store the image data in memory and alter the pointer to hte pixels instead of copying from one buffer to another? My data is kept in a form that's ready to be used by the OpenGL texturing functions, I'm hoping that I can set up glDrawPixels to read that buffer without a need for transformation. I'll check it out later.

Afaik (I'll put a disclaimer here to be in the clear; it's been years since I last did anything OGL. My memory could very well be fuzzed on the topic) the major performance penalty that comes with using glDrawPixels is due to that you have to transfer such a large amount of data over the bus every frame. Also, iirc, there's additional penalties involved due to that the rendering pipeline on the card needs to be flushed and clear before such a transfer can be possible.

In conclusion, I would too vote for the tiled approach.

Again, all this is afaik and iirc.

Share this post


Link to post
Share on other sites
hmmm when i read this the approch which first sprung to mind would be to use textures, however you could be cunning about it [grin]

Load in your data into ram and create a texture which is the next power of two size above the screen size (so, 1024x512 if your screen size is fixed at 640x480). Then, use glSubTexImage() to update only a portion of it, you'll probably want to bench mark to see which size is optimal for updating, however my first thought would be screenwidth + a bit, so you can cheat slightly by scrolling the texture a bit before updates.

So, when you need to update the texture, us the glPixelStore functions to offset into the correct place in your source data and glsubtex the new infomation to the texture (this is where buffering a few extra pixels would be sane, to keep the memory nicely aligned when you update instead of incrementing by a pixel every time).

I'm not sure how fast this would be, so a bench mark might be a good idea, however glsubteximage should be pretty fast on anything post-gf2 so it seems like a viable solution to me.

Share this post


Link to post
Share on other sites
I've just tried the idea suggested by _the_phantom_ and it seems it's too slow. The issue seems to be the giant texture being drawn.

I'm trying a glDrawPixel solution now, but it's still very slow. I'm wondering how else I can speed this up, I'm starting to doubt whether tiling will make any difference either :(

Share this post


Link to post
Share on other sites
yeah, drawing speed is going to be a killer as you are basically filling the whole screen with pixels, glDrawPixels() certainly isnt going to be faster than the texture method because as well as filling the screen you are having to transfer the data across the bus.

Make sure you dont have any none required states set, for example when drawing the background you are going to want blending disabled as its a waste of time (with blending its a horrible read-modify-write cycle, without you just write) and make sure you are only writing the buffers required to try and cut down on the fill rate you are eatting up.

There is a chance that breaking the background up into tiles might well help, as it might allow better resource balancing, however I've never tried it so I'm not sure [smile]

How slow is too slow btw?

Share this post


Link to post
Share on other sites
Anything that includes frequent data transfers between the ram and the card will be slow, so glTexSubImage2D or glDrawPixels will be slow.

If you want max speed, you need the background to be stored in VRAM. For your case,
let's say you have a horizontal moving background of size 2048x480:

On init function:

1)Create a 2048x512 texture.
2)Use glTexSubImage to copy the backround into that texture.

On rendering, just draw a textured quad the size of the screen, altering the texcoords to draw the part of the background you want.

Share this post


Link to post
Share on other sites
The speed was driving me crazy, so I chanced by the NVidia website and got the latest drivers... the speed has increased rapidly now, even using glDrawPixels. I'm guessing that there was some issue with my drivers from the crash I had a few weeks back (computer has never been right since).

I'm going to revisit this again in a few days and implement the glTexSubImage2D solution properly. It was a similar speed to the glDrawPixels before the driver update, so hopefully I'll get the full speed I can out of my crappy card by keeping the texture in VRAM.

Cheers guys, you all just made my credits page =)

Share this post


Link to post
Share on other sites
ooo i've just come up with a genius idea [grin]

Instead of drawing it as once huge texture, break it up into smaller textures so that you have textures which are say 32*512, so you'll probably need 23 of them to make it smooth.
Then, as you draw you draw then strips on screen, scrolling as you need (thus 23 instead of 20 strips to cover the overlap and provide some left/right buffering) and when something falls off left of the screen you refill it with new data from the right.

The overall update frequency of the data is reduced and so is the data transfered, which means any stress will be on the drawing and not on the update.
More strips would mean less requirement to update as often, but higher memory overhead, so you might want to experiment to get a balance.
Also, keep in mind that if you are filtering you'll want a 2 pixel overlap between each strip and a slight inset in the texture coords to ensure things line up, i'll leave the details as an exercise for the reader.

edit:
Just seen your post above, glad to see you've got some speed increase, depending on the whole update method's speed you might want to investigate this idea as well, but thats your call.
Good luck with it [grin]

Share this post


Link to post
Share on other sites
I just realised, the way my data is sotred in memory doesn't seem to be compatible with either glDrawPixels or glTexSubImage2D... I'm loading from TGA with the data stored as RGBA, but annoyingly the image becomes 'corrupt' when the image is bigger than the screen size. I'm guessing it's because of the way the TGAs are saved *sigh*. This is annoying me now :/

But I'll get there!

_the_phantom_, I had that idea myself (indeed that's what I meant by panelling). I wonder how well it'd work in practice.. Worth giving it a shot though [grin]

Edit: How does OpenGL expect the data to be store in memory for glDrawPixels?

If you had a 2 x 2 image, would the data be represented as:

RGBA1 RGBA1 RGBA2 RGBA2

or

RGBA1 RGBA2 RGBA1 RGBA2

I think this is what my problem is...

Share this post


Link to post
Share on other sites
good good, I did infact try to post that answer above pre-down time, so if gdnet hadnt have died it might have saved you a few mins [grin]

The glPixelStore() function and related bits seem to be one of the most overlooked and non-understood functions out there, even I was guilty of ignoring it until recently..

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