Fastest way to handle drawing to a surface every game loop?

Started by Vince Twelve, Sat 10/07/2010 07:19:02

Previous topic - Next topic

Vince Twelve

Hey, with Resonance, I don't do a whole lot of drawingsurface stuff.  I can only think of two places I am using that kind of stuff.  But Infinity Bit is pretty much entirely built around it.  I knew AGS wasn't really intended for that kind of use, but hell, it's just for fun.

But I'm finding my framerate taking a much bigger hit than I expected.  I'm trying to figure out how much of it is due to AGS and how much is due to my approach (I programmed a large portion of the game before I had decided how the game was going to work ;))

Basically, I'm drawing onto the backgrounds of  10 different GUIs.  Not every loop.  Most loops, I'm only drawing one GUI: the one the player is on.

But to do that, I need to take an existing dynamic sprite or create a new one, create a drawing surface from it, draw on the surface, release the surface, and set the background of the GUI to the surface's graphic.  Is there some way of doing this better than by doing all of this each loop?

Basically my code looks like this and is run through rep_ex_always (pseudocode):

Code: ags

function drawFrame(int i){
  sprite = DynamicSprite.Create(w, h, false);
  DrawingSurface *surface = sprite.GetDrawingSurface();
  drawEverything(surface, i); //pass surface on to get all the drawings on it
  surface.Release();
  gui[i].BackgroundGraphic = sprite.Graphic;
}


sprite is declared elsewhere so I can manage its memory.

I've tried keeping the sprites and just clearing the drawing surface each loop, but it doesn't provide any noticeable change in framerate.

How do I do this better?

GarageGothic

Well, the pseudocode can't really be improved on if you say clearing the surface instead of recreating it doesn't improve the framerate. More likely you should be looking at your drawing functions and see if anything could be simplified. When you say you're drawing on ten different GUIs but not all loops, does that there's one loop where you draw to all ten? If so, try to distribute the drawing over several loops.

It would help if you also posted the drawing code, because getting DrawingSurfaces isn't that slow a process in itself.

Calin Leafshade

using 10 guis for drawing onto is not a good way to do it.

Remember even if you dont draw to those guis in the code the engine still needs to draw them which essentially means they get drawn twice.

The code I did for the sprite distortion took a big cpu hit (about 6% on my quadcore 2.8) simply from having 6 full screen guis at 100% transparency each with a single label on (it was for something completely unrelated to the sprite distortion which is why it took me so long to find).

I would suggest you draw to the background instead.

Ryan Timothy B

Yes, I've learned that myself too.  Minimize your GUI count and you'll notice a big difference.

If it must be a GUI, why not just draw on Buttons instead.  Only 10 buttons on one GUI instead of 10 GUI's.  Or just draw on the room background as Queefshade has suggested. ;)

Although, you wouldn't be able to take advantage of 10 layers with different transparency levels by using Buttons, which could be your reason for using GUI's in the first place.  But drawing on a background you can draw with a transparency just as long as the sprite doesn't have alpha channel.

Vince Twelve

Yeah, if you look at screenshots or try the alpha out you'll see why I'm doing it with 10 GUIs. 



Download Alpha

Each layer is framed with an alpha-channeled sprite and has a different transparency.  The whole thing slides around when you move the mouse, so keeping them on separate guis means I can move them around without having to redraw the all ten layers every game loop.  Usually, the background GUIs are drawn once (until the player moves to a new layer, at which point they're all redrawn again), and then the front GUI that the player is on is drawn every game loop since the player is moving around.  I want to have enemies/etc on deeper GUIs moving around, which makes me nervous since each layer with enemies will have to be redrawn as well.  I suppose I could reduce it to drawing every other loop and see how that looks.

Quote from: GarageGothic on Sat 10/07/2010 07:47:18
It would help if you also posted the drawing code, because getting DrawingSurfaces isn't that slow a process in itself.

The drawing code is just drawing rectangles to form the level based on a tile map and then a few sprites (non scaled on the GUI that gets drawn every loop).  If I comment out all the drawing code and just leave the creation of drawingsurface etc code from the pseudo code above, I only see a one, maybe two, fps increase.

The advantage of the GUI setup (I thought) is that I don't need to draw those other levels, so they're stored on the GUIs and can get moved around freely.  And I can easily change their transparencies.

And leaving the GUIs there just with the alpha-channeled sprites on them but removing the drawingsurface code gets the frame rate right up there to max, so the GUIs themselves don't SEEM to be hurting the frame rate.   I'm wondering though if it wouldn't be worth all the work it would take to redo the drawing code to just draw the whole shebang to the background.

Ryan Timothy B

Okay, I've played the game very briefly just now.  I can't control the guy the way I want to at all.  He's either lightning fast or standing still, there appears to be no acceleration.  I also have no idea how to save the game and stuff, I'm always dying on the second level.  Perhaps your game thread speaks about this, I'll have to check it out and post in it later when I'm done work (on lunch right now).

I'm only getting 20 fps and my machine can run some pretty crazy AGS tests that I've done with DrawingSurfaces at the full 40fps or higher.  I'm assuming you've either lowered the gamespeed to stay at 20, or it just lags that badly with those GUI's.

Nothing as far as I've gotten appears to have alpha channel except for the outer box perhaps, but that seems to be drawn at 100% opacity even when you die and it shrinks down.  From what I've seen, it looks like you could easily draw on the room background instead of the GUI's.

Vince Twelve

Yeah, it's an alpha.  Lots of features like saving not implemented yet.  And I tweaked his speed and acceleration on a machine that was getting 15 fps (though I didn't notice how bad the frame rate was at the time) so it feels quite comfortable at that speed.  :P  I'll retweak when I know what kind of framerates to expect.

The outer boxes have alpha channels to have smooth corners, but I could axe that in the interest of frame rate.  But even turning those graphics off, I'm getting similarly slow rates.

But regardless, I gave it a go and changed everything to draw on the background.

I removed all the GUIs and had just the main level (no frames, just the rectangles and character/object sprites) draws to the background every game loop, everything speeds up considerably.  30-34 on my machine.  However, if I'm drawing to the background, I need to draw all the levels every loop.  When I make all the levels get drawn to the background every loop the frame rate drags even lower than it is now.  I was getting 8-9 fps.

It may just be that I'm trying to do something that AGS is incapable of handling speed-wise, which is fine.  It's just a little for-fun project and programming challenge.  I'll release source code when the game's done and maybe someone can look and find a way to improve the speed.

Ryan Timothy B

Hmm.. That's weird.  I've drawn approximately about 600 30x30 sprites per game loop without any slowdown on this laptop.

One thing that does speed things up is keeping the drawingsurface open the entire time you're drawing on the background, instead of opening it each time you draw a new sprite.  Which is what your code above looks like it's doing already.

Oh, and another thing that 'could' speed things up (don't quote me on this, I haven't fully tested it) is instead of using:
sprite = DynamicSprite.Create(w, h, false);
each game loop, you can save it to a DynamicSprite and just reuse it each and every time.  I'll have to give it a speed test one of these days and see if I'm right or wrong.

GarageGothic

I don't really get how this can be so slow either. Possibly using characters instead of GUIs would speed it up slightly (especially if you resize your sprites before/when drawing them). It's a bit hard to work out what exactly goes into the process just looking at the demo. Are the sprite tinted in-engine? Again, this could be faster with characters and the built-in tint function.

Vince Twelve

Hrm, I don't really understand why it dropped so much when I changed to drawing 10 layers to the background either.  Maybe when I changed the function to tell it to draw all, it caused some kind of loop that's drawing them all multiple times or something.  I'm going away on vacation for the next couple days and will spend nights rewriting the draw code to see if I can figure this out.


One thing different between the front layer and the other nine, is that the deeper layers all use scaled sprites while the front uses unscaled.  Might that slow it way down?

Mostly, my code just passes the surface around to several different custom functions for drawing rectangles/lines/sprites at the correct scaling for the requested layer.  There may be 1000 or more rectangles on the screen at once plus maybe 100 or so sprites, most scaled.  I could do some testing to consolidate rectangles into fewer larger rectangles, but I wasn't of the impression that the rectangles were the problem.

Ryan Timothy B

Quote from: Vince Twelve on Sun 11/07/2010 03:24:23
One thing different between the front layer and the other nine, is that the deeper layers all use scaled sprites while the front uses unscaled.  Might that slow it way down?
If you're scaling them every game loop, I'd say that would definitely be one reason it could be so slow.

When I played it earlier it looked as though the back layers don't change at all while you're playing in the front layer.  I would be creating and scaling those layers only once during the change of each level and keeping them in memory to be used each cycle.

SMF spam blocked by CleanTalk