Slick Forums

Discuss the Slick 2D Library
It is currently Sun May 19, 2013 7:28 pm

All times are UTC




Post new topic Reply to topic  [ 3 posts ] 
Author Message
PostPosted: Fri Sep 21, 2012 9:01 am 
Offline

Joined: Mon Sep 10, 2012 10:47 am
Posts: 16
MOD: moved from bugs to help section
Hi,

sorry for the thread necromancy, but I'm trying to understand this bug. I'm new to slick and so I'm not sure I understand under which conditions the memory leak will occur. I'd like to be prepared. :)

I'm planning to do offscreen rendering in my game. It will be (yet another...) 4X space game, and on the overview map each star will have statistics displayed (text and small images: name, number of planets, resource symbols, etc.) which will not change very often. So my idea was to pre-render an image for each star at the start of a player's turn so that in my render() method, I have to render only one image per star. Pre-rendering is not performance critical, as it has to be done very rarely.

So if I understand the first post correctly, I shouldn't do this using Image.createOffScreenImage() and a subsequent getGraphics() because this triggers the memory leak, right?

What about the method using the container's graphics object and copyArea(), as described here: http://ray3k.com/site/tutorials/slick-offscreen-rendering/ ? Will this lead to a memory leak? It feels a bit strange to render something to the screen which remains invisible because it's outside the render() method, but if it's the bug-free way to go I'll use this.

I also plan to use TWL by the way, so I hope this won't interfere somehow.

Thanks in advance,

-Kylearan


Top
 Profile  
 
PostPosted: Fri Sep 21, 2012 8:27 pm 
Offline
Slick Zombie

Joined: Sat Jan 27, 2007 7:10 pm
Posts: 1467
With Java2D it's common to "pre-render" things to improve performance. In OpenGL, it's often more efficient to render the elements directly to the screen. Here are some considerations:

1.
To create an off screen image, we tell OpenGL to allocate a new texture. Since Slick ensures all textures are power-of-two-size (since older drivers may not support NPOT), this means that an offscreen image the size of the window (e.g. 1920x1080) will be padded to 2048x2048. Likewise, a 300x65 image will be padded to 512x128. This could potentially be a lot of wasted space.

2.
One of the major causes of poor performance in 2D games is "texture thrashing." Let's say we load two PNG images with Slick -- both will be allocated to separate textures. Because of the way Slick uses OpenGL (i.e. "old school immediate mode"), we can only bind a single texture at a time. So, in order to render the two sprites one after another, OpenGL first needs to bind texture A, then draw it, then bind texture B, then draw it. The more textures we need to bind per frame, the more our game will slow down. This is why we tend to use sprite sheets and startUse/drawEmbedded/endUse -- it allows us to bind a single texture per frame and re-use it for many images.

Minimizing texture thrashing is a very common way to optimize games/graphics. There are now extensions that allow for multi-texturing, but they may not be reliable for older platforms and Slick's codebase isn't really suitable for that kind of OpenGL usage.

So, if you were to create all of your "pre-rendered" images in the same texture, you would not lose much performance. But this brings me to my next point...

3.
You may run into a bottleneck eventually rendering many sprites, but chances are this bottleneck will lie with the GPU's "fill rate" limitations. This is simply the number of pixels you can render every frame. If you are using full-screen resolution and drawing each tile many times (e.g. for lighting effects), you may very quickly run into fill rate limitations. Offscreen rendering will not improve your performance since you will still be drawing the same number of pixels (or more).

4.
OpenGL is fast. Very fast. If you're using drawEmbedded, you can push tens of thousands of small sprites per frame. Most games will never have that much going on at once; and often you will be using simple culling (i.e. "not in view") to further improve your performance. So, really, you shouldn't be using offscreen images unless absolutely necessary -- this all falls under "premature optimization."

Quote:
So if I understand the first post correctly, I shouldn't do this using Image.createOffScreenImage() and a subsequent getGraphics() because this triggers the memory leak, right?

The memory leak is not a huge deal; it only creates a single extra texture at instantiation (since you should never be creating a new image every frame). The problem arises when you use "new Image(int, int)" (which creates an OpenGL texture) and then "getGraphics" (which creates another OpenGL texture). The solution is to use the utility method, like so, which will only create a total of one texture:
Code:
//instead of "new Image(512, 512)"
Image image = Image.createOffscreenGraphics(512, 512);
imageGraphics = image.getGraphics();


Ideally Slick will be patched up so that "new Image(512, 512)" works without any issue; at which point the above utility method will be deprecated (but provide the exact same functionality). But this patch may not come for a while since it requires a pretty substantial change to Slick's codebase.

Regarding copyArea; that method never invokes getGraphics and so no extra textures are created. You don't need to use createOffscreenGraphics if you plan to use copyArea.

TL;DR - Don't bother with offscreen graphics unless you absolutely need to, since rendering to the screen is almost always a better option. If you really need to use offscreen images, then don't worry too much about the memory leak, it will hopefully be fixed at some point. If you want the best practice, for now you can use createOffscreenGraphics(..) instead of "new Image(w, h)".



EDIT:

Also regarding copyArea, it's there if you need it, but generally it's better to use offscreen graphics and render directly to them. Compare the following: (a) drawing to the screen, and then copying all of those pixels to an image, or (b) simply drawing to an image. (b) is faster since it skips the copying.


Top
 Profile  
 
PostPosted: Mon Sep 24, 2012 7:44 am 
Offline

Joined: Mon Sep 10, 2012 10:47 am
Posts: 16
Hi,

thanks a lot for your detailed answer! I only know the absolute basics of OpenGL and chose to use slick and TWL so that I wouldn't need to learn more. But of course OpenGL-specific artifacts will come up even when using wrapper libraries, so your answer helped me quite a bit.

davedes wrote:
Since Slick ensures all textures are power-of-two-size (since older drivers may not support NPOT), this means that an offscreen image the size of the window (e.g. 1920x1080) will be padded to 2048x2048. Likewise, a 300x65 image will be padded to 512x128. This could potentially be a lot of wasted space.

That's a good point I hadn't considered.

Quote:
So, if you were to create all of your "pre-rendered" images in the same texture, you would not lose much performance. But this brings me to my next point...

3.
You may run into a bottleneck eventually rendering many sprites, but chances are this bottleneck will lie with the GPU's "fill rate" limitations. This is simply the number of pixels you can render every frame. If you are using full-screen resolution and drawing each tile many times (e.g. for lighting effects), you may very quickly run into fill rate limitations. Offscreen rendering will not improve your performance since you will still be drawing the same number of pixels (or more).

Indeed I had planned to pre-render my images to the same texture, and I don't think I would run into the maximum fill rate problem because the pre-rendering would happen only once per player turn (in my turn-based strategy game), so only every couple of seconds or minutes even, not every frame.

But...:

Quote:
[...] this all falls under "premature optimization."


I guess you're correct with this. :) So I guess I'll start by rendering everything to the screen every frame and see if I really need to optimize. I'll have to come up with a good source code architecture so that I won't have to refactor too much when I really have to change the way I render things, but that's a different problem I feel more at home.

-Kylearan

P.S.: Also thanks for all the answers you gave to other people all over this forum; spared me a lot of asking. :wink:


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 0 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group