Shoebox memory leak

Previous topic - Next topic

MrTAToad

The more beta testers the quicker the final release :)

Crivens

QuoteWhen you "draw" the sprite to an offscreen surface, draw with alphamode -1 to preserver the alpha bits
Just got round to testing that out. Brilliant that's a lot better for my blended sprites (see attached example).

However, I still don't know how to resize the sprites so that they use up less memory and CPU. ie. there is no point using full retina graphics on a lower resolution but I don't really want to have 2 lots of graphics to load (esp with new Android market looming and much more resolutions to handle). My previous code would resize and save out to a different directory and this worked fine, except I can't see how this would work with alphablended sprites as seen in my attachment. Surely pasting a sprite (stretchsprite) at a smaller size to the screen (as I do) and then grabbing it at the smaller size to then savesprite would lose all the different levels of transparency? If you paste onto pink, then that's one thing, but surely again only the exact (255,0,128) would be transparent and the semi-transparent stuff would be an off-pink that would stay that colour when loaded back in again? In other words how do I save an alphablended sprite in GLB so that it fully retains all the different levels of transparency, but it's now at a different size than the original sprite? eg. either loadsprite or savesprite with a different ratio percentage for both X and Y that will load or save a sprite at a set size. Much better for RAM and CPU then then stretch or zoom sprite. Or is there another way of doing this?

Cheers

[attachment deleted by admin]
Current fave quote: Cause you like musicians and I like people with boobs.

Ian Price

One option is drawing your scene to a retina size image and scale down the whole thing down for non-retina devices? Obviously your X and Y co-ords would have to take into account the display differences. Scaling down is going to lose detail no matter what you do.

Another option is to release two versions - retina and non-retina.

As for Android, with so many different resolutions, there's not going to be one single/easy answer to any res problems.
I came. I saw. I played.

Crivens

Yes, that is what I did in the code I posted. Basically you have your media directory with everything at retina sized graphics. Then you check the resolution compared to retina. If it is retina then all is good, carry on. If not then load all graphics and stretch sprite them to a pink screen at the appropriate ratio to match retina for your current resolution, and then savesprite to a different directory. Once finished set the current directory to the different directory. This works fine with a demo app I made for the 3GS and the Touch 4G (Retina). Only thing that doesn't work is a PC where for some reason the saved PNG doesn't work with pink for transparent in GLB (even though is 100% correct RGB value). Just load into PSP and save it straight away and GLB works with it again. Strange, but don't care as this is for iPhone, and it works there.

However with alphablended sprites which have multiple levels of transparency (see above screenshots) then this method will not work. As far as I know (correct me if I am wrong), then grabsprite just uses the colours on the screen and doesn't care about actual transparency. So this is provided by pink (255,0,128). So how can I grab a sprite and retain the multiple levels of transparency? Either to then save as an alphablended png, or to store as a sprite in memory retaining the multiple levels of transparency. Then the sprite has the same transparency as it's bigger version, but is using up less memory and therefore must be less usage on the CPU/GPU when drawing it.

Overall though this is to reduce the time it would take to produce different sized graphics yourself. And with the massive potential different resolutions of different devices then you basically wouldn't have to worry about it. My initial code proves this, but it just stalls with alphablended sprites.

Cheers
Current fave quote: Cause you like musicians and I like people with boobs.

Ian Price

QuoteYes, that is what I did in the code I posted. Basically you have your media directory with everything at retina sized graphics. Then you check the resolution compared to retina. If it is retina then all is good, carry on. If not then load all graphics and stretch sprite them to a pink screen at the appropriate ratio to match retina for your current resolution, and then savesprite to a different directory.
No, that's not what I meant at all.

You just load in the graphics for retina display and draw your WHOLE scene to a virtual screen.
So (CREATESCREEN ID_SCREEN,SPRITE_ID,960,640), then USESCREEN ID_SCREEN. Draw EVERYTHING to this screen, then USESCREEN -1, then scale down SPRITE_ID using POLYVECTOR (or ZOOMSPRITE, but that's slower). This way you aren't saving anything to anyhere and you still get all transparency etc.
I came. I saw. I played.

ampos

will it be fast enough?
check my web and/or my blog :D
http://diniplay.blogspot.com (devblog)
http://www.ampostata.org
http://ampostata.blogspot.com
I own PC-Win, MacBook 13", iPhone 3G/3GS/4G and iPAC-WinCE

Crivens

I've used createscreen before, but I don't quite get what you mean here. Would this allow alphablended sprites that are say 500x500 to be then loaded as sprites that are 250x250 (and not actually be stored as 500x500 behind the scenes which is what happens with stretch or zoom sprite for example)?

Or are you saying use this screen to draw everything at say 50% size then use that screen? Wouldn't that be pretty much the same as using stretchsprite with a 50% size on X and Y? Effectively you are still holding in memory the sprite at 100% size, and everytime you want to draw it at 50% then behind the scenes it would have to calculate every time to showing it at 50%. Or am I missing something?

Effectively at the end of the day I am looking for a routine that takes retina graphics and resizes them on the fly to the current resolution so it doesn't take up the same memory or processing power that keeping the actual retina graphics in memory would do (using stretchsprite at 50% every time you draw the sprite is still storing it at 100%). I did this before but it won't work with multiple levels of transparency.

Cheers
Current fave quote: Cause you like musicians and I like people with boobs.

ampos

You open a "hidden" screen.

Createscreen 1,5,640,960

This screen (640x960) is both a screen (id 1) and a sprite (id 5 in my example).

When you use USESCREEN 1, everything you draw is drawn into this screen/sprite, not in the backbuffer. Once you finish your drawing operations, you use USESCREEN -1 to draw again in the backbuffer: it is time to use STRECTSPRITE 5,0,0,320,480 to draw the sprite id 5 (remember, it was also a "screen") resized to the "real" screen.

Code (glbasic) Select
Createscreen 1,5,640,960 //retina screen
usescreen 1  //draw on the previous created screen
drawsprite 1,100,100  //draw something in this screen
print "Works!",10,200 //something else to be drawn
usescreen -1 /go back to the real backbuffer
strectsprite 5,0,0,320,480 //draw the sprite downscaled, you can use also zoomsprite or polyvector
showscreen


there was also a command to disable background cleaningm but I dont remember. That way screen refresh is faster.
check my web and/or my blog :D
http://diniplay.blogspot.com (devblog)
http://www.ampostata.org
http://ampostata.blogspot.com
I own PC-Win, MacBook 13", iPhone 3G/3GS/4G and iPAC-WinCE

Ian Price

If you draw everything at full size onto the canvas (virtual screen), then scale that canvas down you are only scaling once. You are not scaling the sprite, only the virtual screen that all the sprites are on.

This should still be very fast using POLYVECTOR (although this will depend on how much drawing is done to the virtual screen).

Code (glbasic) Select
LOCAL frames_per_second

// Set main game screen at 640,480 res
SETSCREEN 640,480,0

// Create a virtual screen that is twice as large as game screen
CREATESCREEN 1,999,1280,960

// repeat
WHILE TRUE

// Use the virtual screen for drawing
USESCREEN 1

// Draw 100 rectangles each frame
FOR n=0 TO 100
DRAWRECT RND(1280),RND(960),32,32,RGB(RND(255),RND(255),RND(255))
NEXT

// Set drawing to proper visible screen
USESCREEN -1

// Ensure non anti-alias
SMOOTHSHADING FALSE

// Draw image of virtual screen, but scaled by half
STARTPOLY 999

//         //Pos+dimenson    // Virtual screen dimensions
POLYVECTOR 0,0,              0,0
POLYVECTOR 640,0,            1280,0
POLYVECTOR 640,480,          1280,960
POLYVECTOR 0,480,            0, 960
ENDPOLY

// Proof that the original screen is being scaled by 1/2 - Black rect is twice the size of small rects.
DRAWRECT 200,200,32,32,RGB(0,0,0)

// calculate and show FPS
frames_per_second=FPS()
PRINT "FPS="+frames_per_second,10,10

SHOWSCREEN

WEND


// FPS counter
FUNCTION FPS:
STATIC OldTimeReport$,FPSDat,TimeReport$,FPSd
OldTimeReport$=TimeReport$; FPSDat=FPSDat+1;
TimeReport$=PLATFORMINFO$("TIME")
IF OldTimeReport$<>TimeReport$; FPSd=FPSDat ; FPSDat=0; ENDIF
RETURN FPSd
ENDFUNCTION


That code shows the virtual screen being blitted by 100 rects (using DRAWRECT, which is slower than drawsprite). The virtual screen is then scaled to fit the actual/visible screen and displays a black rect to prove that the scaled screen is exactly half of the original image (do a screen capture and measure the small rects and large). It also shows FPS, which should stay at a constant 60FPS.

There's no alpha transparency involved in the example, but it doesn't matter. The scaled image will look EXACTLY like the original image, just reduced in size.
I came. I saw. I played.

Crivens

I get what you are saying but surely putting everything onto a virtual screen won't be as fast as just drawing a correctly resized sprite directly to the backbuffer? Plus if you are doing this on a moving sprite or a changing background then aren't you creating this virtual screen everytime from the same retina sized sprites? So you are still holding the full sized sprites in memory. If the original sprite is 500x500 for retina, then I really only want a 250x250 sized sprite loaded into memory on devices below retina. Otherwise it's a waste of memory. I don't see the point of loading and then rescaling a sprite or a virtual screen when you can have the original 500x500 sprite, and also the 250x250 sprite (automatically resized and saved) and it knows to use 250x250 when not on retina. This is no different to actually using 2 sets of sized sprites that you create yourself and choose in the program based on resolution, but why not make it do it automatically (like I did earlier just not with alphablend) and cover other resolutions while you are at it?

Cheers
Current fave quote: Cause you like musicians and I like people with boobs.

Ian Price

You can't have it both ways - low memory usage (with only one set of sprites) and/or scaling larger to smaller without some sort of payoff. You could have two different apps with different sized images if you like (which will cost you more of your own time (redrawing everything twice) than GLB doing it in real-time), but what you want you can't have in the manner that you initially stated.

My example above is plenty fast enough with a hundred low-speed DRAWRECTs.  Having your sprites animating and the game running will have an effect, but with careful management it shouldn't be an issue at all. And remember if you use POLYVECTOR and POLYNEWSTRIP you could draw thousands of sprites on screen quicker than just using DRAWSPRITE, even on iDevice.
I came. I saw. I played.

Crivens

The method I'm describing is purely to save memory when on a lower resolution device. I already wrote a simple program that resizes sprites so that everything is the correct ratio using smaller sprites on non-retina. And it all looks and runs perfectly fine. I run it on either retina or non-retina devices and they look identical from one set of retina sized graphics (obviously the lower resolution device will not look as smooth). But the retina device has the graphics full sized in memory whereas the non-retina device is using a lot less memory with the resized sprites.

The only thing missing is grabsprite (and from my example savesprite) does not include the correct opacity settings from the original sprite. Obviously it cannot as it just grabs what is on the screen. Opacity is only really measured by making pink into fully transparent. Not good enough for alphablended sprites with multiple levels of opacity. If there is another method to load a sprite, resize it, save it in the new size but keep the opacity information then great, that's what I want. I'm not bothered with losing anything detail wise as you obviously do that when going to a smaller resolution, but keeping the different opacity information I do want! This is why if it cannot currently be done I wanted to know if Gernot could modify the savesprite command to save a loaded sprite at a specific size (both X and Y ratios given separately). This way you could load the sprite, keeping all it's opacity levels as it does now, get the X and Y ratio for the resolution and save out the new resized sprite at those ratios to create a new smaller sprite that has the originals opacity info. From then on the app can load the smaller sprites you saved on initial installation, so saving memory. Working out coords and mouse positions and the like are pretty easy after that.

Cheers
Current fave quote: Cause you like musicians and I like people with boobs.