|
|
|
#1 |
|
Senior Member
Join Date: Sep 2000
Posts: 1,339
|
AS3 is FAST! Redux - CopyPixels vs. Sprite Pooling
The original thread is getting off topic and this contained enough new data I figured I'd make a new thread and sum it up. Hope that's ok
![]() Basically, several people have suggested using the copyPixels method on BitmapData and compare it to my sprite pooling approach. So I went ahead and did just that. The results are interesting to say the least. Copy Pixels http://www.electrotank.com/junk/mike/flash/copyPixel/ Sprite Pooling http://www.electrotank.com/junk/mike/flash/objectpool/ If you set the number of arrows the same on each, you will see the copyPixels is the clear winner when it comes to frame rate while time spent in the onEnterFrame is significantly less with object pooling. Many of the frame-rate junkies here disregard anything else as important but I feel the object pooling method still seems preferable in cases where you dont have floods of sprites or movie clips and you need to shave milliseconds. Either way, I go into a good detail on how it all works and my speculations here. Have fun!
__________________
Michael Grundvig Electrotank, Inc. http://www.electrotank.com mike@electrotank.com ElectroServer 4 multi-user server - forum |
|
|
|
|
|
#2 |
|
Pumpkin Carving 2008
Join Date: Apr 2006
Location: Grand Rapids MI
Posts: 2,284
|
That's really good research, web. If I finally migrate to AS3, I'll be glad started collecting all theses threads in a doc file on my pc. But... frame-rate junkies? Is that all we are to you?
|
|
|
|
|
|
#3 | |
|
Senior Member
Join Date: Sep 2000
Posts: 1,339
|
Quote:
If we are going to start building the type of games I like (RTS, large RPGs, etc.) in Flash then we need to be able to squeeze every drop of performance available. That means understanding the internals as best we can. Thanks!
__________________
Michael Grundvig Electrotank, Inc. http://www.electrotank.com mike@electrotank.com ElectroServer 4 multi-user server - forum |
|
|
|
|
|
|
#4 |
|
Moderator
Join Date: Jul 2001
Location: Estonia
Posts: 8,193
|
I was planning to do some test about it myself. Just so I understand what method you are using, let me see if the basics are correct:
1. Sprite Pooling. *You have separate sprite object for each arrow. *Bitmap from memory is loaded into each arrow bitmapdata object. *Arrows are added to stage using addChild. *Arrows are moved by updating their x and y properties. *Scrollrect property is changed for each arrow at every step. 2. Copy Pixels *1 general arrowsprites bitmapdata object is created. *You have general object for each arrow (not the sprite object). *Arrow sprites are not added to stage. *At the stage is visible just 1 big bitmap object. *Arrows are moved by updating their x and y variables. *Main bitmap is cleared at each step. *CopyPixels is used to copy image from arrowsprites object to main bitmap into position of each arrow. Is the description correct so far? |
|
|
|
|
|
#5 |
|
Script kiddie
Join Date: Jun 2004
Location: England
Posts: 2,587
|
Something that's being overlooked in both these tests, though, is memory.
Using OS X's Activity Monitor, I watched Safari running both examples (500 frames, 50 arrows per frame). For object pooling, the real memory stayed firm at about 88 MB and the virtual memory went to about 400 MB. But with copyPixel, the real memory went from about 160-400 MB (kept incrementing steadily then dropped down again; probably the garbage collector) and the virtual memory reached 600 MB. There's something I'd like to point out... I think of it as the 'spike', and I think it's caused by the garbage collector clearing up. It happens in the copyPixels example, and the object pooling example when pooling is disabled. Every few seconds, the movie freezes for about half a second (and I think it's at this point that the copyPixel goes from using 400 MB back to 160 MB of RAM). You don't get this with pooling, because objects aren't being removed. The memory usage stays constant, so you don't get freezes every few seconds. If you haven't noticed this (I'll be honest, I had to pay close attention to notice it in the copyPixel test), try using 500 arrows in the object pooling test, with pooling disabled. Much more noticeable there. Also, am I noticing that the copyPixel example doesn't seem to have an alpha channel? The arrows don't fade out, and there's no anti-aliasing. I'd trade the 3 fps speed difference I get for the graphical improvement object pooling allows. ![]() So, round-up... Sprite Pooling: Consistently low memory usage Alpha channel Significantly faster enterFrame execution About 3 fps slower No memory spike CopyPixels: Erratic memory usage No alpha channel Slow enterFrame 3 fps speed boost Memory spike Maybe this is just me not having the will to do another rewrite of Invasion 5, but I still prefer object pooling. The average user can't be expected to use as much as 400 MB of RAM for a Flash game (although this is in extreme cases; realistically you wouldn't use so many sprites under any circumstances - performance results across computers would be too inconsistent), and they won't appreciate the lack of an alpha channel. Plus, believe it or not, some people don't seem to notice a 3 fps speed difference. :s And Tonypa, that all seems correct to me, but you've left out the whole point of the sprite pooling method - the pool. When objects would usually be removed, they are instead moved into an array to be re-used when the next object is created. |
|
|
|
|
|
#6 |
|
M.D.
Join Date: Dec 2002
Location: Shelter
Posts: 4,060
|
maybe its just me but i think one of the reasons why the pooling one works better is that there's 1200 less arrows on stage.
or am i missing something here. |
|
|
|
|
|
#7 |
|
Script kiddie
Join Date: Jun 2004
Location: England
Posts: 2,587
|
Nah, that's because object pooling defaults to 25 arrows per frame and copyPixels to 50 arrows per frame... used 50 for both when I did my test.
|
|
|
|
|
|
#8 |
|
Member
Join Date: Jan 2004
Location: Copenhagen, Denmark
Posts: 50
|
Ahr, Vengeance MX, that's not entirely true. You can use the alpha channel with CopyPixels. And for memory usage - just have a BitmapData that you always draw to. I had the same spikes when I always threw away my BitmapData canvas. Just do a .fillRect with your favorite background color and use it again.
That's what I'm doing here: http://enwire.dk/flash/Flocking.html Alright, I'm actually using the .draw method on BitmapData so I can use BlendMode.ADD, but I strongly suspect that when the input to the draw method is a BitmapData then it's just a form of copyPixels underneath. |
|
|
|
|
|
#9 |
|
M.D.
Join Date: Dec 2002
Location: Shelter
Posts: 4,060
|
to use alpah in copyPixels set the copyAlpha property to true
myBitmap.copyPixels(copy, rect, point, null, null, true) its false by default. i dont see how more memory could be created unless your re-creating bitmaps and not disposing before you do. THere used to be a memory leak in F8, not sure about F9 |
|
|
|
|
|
#10 |
|
Member
Join Date: Jan 2004
Location: Copenhagen, Denmark
Posts: 50
|
It's extremely easy to spike the memory. In my first try at it I went from 0 to 500MB in seconds. All it takes is to allocate a new BitmapData of a reasonably large size (800x600) on each EnterFrame. Sure, the old ones will be deallocated - eventually. So in the best case you will see spiking before the garbage collector kicks in a frees the memory of the old BitmapData - but if you're going too fast (30FPS @ 800x600) you can easily hit virtual memory before the garbage collector gets a chance to run. And lets just say that when that happens things (and especially FPS) go down hill fast.
The solution is of course to "pool" your BitmapData by reusing them in each frame. Either completely copying them over with new data, or if it's for a canvas type thing, simply clear them with a fillRect. ------------------------ Ahr, hadn't actually seen there was a dispose method on the BitmapData. Don't know how that would work, but there shouldn't be too much difference between reallocating and doing fillRect on an existing BitmapData. Would need to speedtest it though to see if that's true ... And just tested dispose. You're absolutely right - no memory spikes at all. Thanks
Last edited by YaiEf; 05-24-2007 at 08:02 AM. |
|
|
|
|
|
#11 |
|
Script kiddie
Join Date: Jun 2004
Location: England
Posts: 2,587
|
Not sure about alpha with copyPixels... first time I used copyPixels (in my BitmapData blitting experiment), something along the lines of this happened (Photoshopped picture, couldn't be bothered to actually reproduce it):
Transparent pixels overwrote each other instead of overlaying each other. Does the copyAlpha property change that? And yes, I'm surprised that the memory usage in the copyPixel experiment is changing so much... thought it'd just be a few bytes to store each arrow's properties, plus a few KB for the spritesheet, and maybe a few MB for the 800 x 600 BitmapData object. |
|
|
|
|
|
#12 | |
|
Moderator
Join Date: Jul 2001
Location: Estonia
Posts: 8,193
|
Quote:
|
|
|
|
|
|
|
#13 |
|
M.D.
Join Date: Dec 2002
Location: Shelter
Posts: 4,060
|
heres my attempt
both copyPixels onto another bitmap (blitting) http://soap.com.au/chris/arrow.html //5000 arrows, no rect change http://soap.com.au/chris/arrow2.html //2300 arrows, changing rect no sprites involved, which means no scrollRect. I should do up a scrollRect version mem usage 14mb, 50% cpu so i dont know how the other can be 500+, thats just weird. Last edited by mr_malee; 05-24-2007 at 09:23 AM. |
|
|
|
|
|
#14 |
|
M.D.
Join Date: Dec 2002
Location: Shelter
Posts: 4,060
|
scrollRect version, not so good
http://soap.com.au/chris/arrow3.html //2300 arrows, scrollRect i dont think i'm doing it the wrong way mem usuage 30 and climbing, i think theres something going on here with cacheAsBitmap, scrollRect forces this, so maybe when the scrollRect changes (x position) its re-caching maybe adding more memory? Last edited by mr_malee; 05-24-2007 at 09:34 AM. |
|
|
|
|
|
#15 |
|
Senior Member
Join Date: Sep 2000
Posts: 1,339
|
Blazing fast update by tweaking how I do alpha and using the fillRect trick suggested above by YaiEf. Thanks!
http://www.electrotank.com/junk/mike/flash/copyPixel/
__________________
Michael Grundvig Electrotank, Inc. http://www.electrotank.com mike@electrotank.com ElectroServer 4 multi-user server - forum |
|
|
|
|
|
#16 |
|
Script kiddie
Join Date: Jun 2004
Location: England
Posts: 2,587
|
God damnit.
I'm gonna have to rewrite Invasion again, aren't I? ![]() Webgeek, what did you change about how you handle the alpha? Just the copyAlpha property? Or something else? |
|
|
|
|
|
#17 |
|
Moderator
Join Date: Jul 2001
Location: Estonia
Posts: 8,193
|
Well, I did build my own speed experiments
![]() I used 5000 sprites and measured time spent to do 200 enterframe steps at movie fps set to 40. Spritesize was 32 pixels and stage size was 480x360 pixels. All tests ran using standalone FP 9.0.45.0 WinXP. Test1. Created the bitmap objects, added them all to the stage at random positions. Each bitmap had separate bitmapdata object (small one, size of single sprite). All bitmaps had cacheAsBitmap set to false and transparent set to true. First ran the loop without changing the position or content of objects: ~5,3 seconds. Then ran same loop and at each step changed the position of objects randomly: 20~21 seconds. Some additional tests with many sprites: *changing transparent to false in every sprite reduced overall time to ~19 seconds, transparent bitmaps are slightly slower. *changing cacheAsBitmap to true in every sprite increased overall time to ~24 seconds (not sure why) Test2. Created 5000 simple objects and assigned random values for xpos and ypos variables. Created 1 stagesized bitmap and added it into stage. This bitmap is cleared using fillRect at each step. First ran the loop without changing variables in the objects: ~5,1 seconds. Slightly faster then looping through bitmaps, but by very small margin. Then ran the the loop and changed random variables in each object, used copyPixels to copy sprite image from main sheet into stage bitmap at position of each object: ~25 seconds. Test2 was slower overall. If you want to move around images which do not change itself (only the position), use many bitmaps. Test3. This was same as test2, but at each step the content of each object was also changed randomly. Not surprisingly, time was exactly same. Test4. Tested many bitmaps which all change the position AND the content at each step. All bitmaps had cacheAsBitmap set to false and transparent set to true. Each step changed the position of objects randomly and used copyPixels to update the content with random image: 36~37 seconds. Test5. Tested many bitmaps changing content using scrollRect object. Each bitmapdata object same as main spritesheet and scrollRect was used to pick image to show. Test unfinished, ran out of memory. Estimated time to finish: ~couple days. If you have objects that change the content a lot beside moving around, use single large bitmap and copyPixels method. |
|
|
|
|
|
#18 | |
|
Senior Member
Join Date: Sep 2000
Posts: 1,339
|
Quote:
myBitmap.copyPixels(copy, rect, point, null, null, true); Nice, simple, and fast.
__________________
Michael Grundvig Electrotank, Inc. http://www.electrotank.com mike@electrotank.com ElectroServer 4 multi-user server - forum |
|
|
|
|
|
|
#19 |
|
Pumpkin Carving 2008
Join Date: Apr 2006
Location: Grand Rapids MI
Posts: 2,284
|
Is there some trick to the sprite sheet used for the arrows? I understand that it's built in rotating fashion, and that it simply scrolls through each "frame" of the sheet, but how do you go about determining the appropriate frame to display, and wouldn't you need an entirely new spritesheet if the gravity/velocity was changed?
|
|
|
|
|
|
#20 |
|
Script kiddie
Join Date: Jun 2004
Location: England
Posts: 2,587
|
IP, that'd be my script.
![]() Here's the sprite sheet, rotates 5º every frame: frame = Math.round((Math.atan2(yspeed, xspeed)*radToDeg+90)/5); if (frame<0) frame = 71-Math.abs(frame); The second line of code fixes a frame selection problem that occurs because Flash's rotation system goes from 0 to 180, -180 to 0. |
|
|
|
![]() |
|
||||||
| Thread Tools | |
| Display Modes | |
|
|