Can someone tell me why this is sooooo slow?
BlitzMax Forums/BlitzMax Programming/Can someone tell me why this is sooooo slow?
| ||
Function DoIris() Local x = 200,y = 150,d = 1000 Local center_x = 400,center_y = 300 Local circle:TImage = CreateImage(800,600,1,MASKEDIMAGE | DYNAMICIMAGE) GrabImage(circle,0,0) Repeat d = 1000 Repeat Cls SetColor(255,0,255) ' The mask color x = center_x - (d / 2) ' Convert from silly 'circle in box' to more conventional circle center y = center_y - (d / 2) DrawOval(x,y,d,d) ' Draw the circle on the back buffer d:- 5 GrabImage(circle,0,0) ' And then copy it to circle:Timage ' DrawLevel() ' Now, draw the game level (This is NOT the bottle neck here...) SetColor(255,255,255) ' Since current color affects drawing operations... DrawImage(circle,0,0) ' Now draw the circle image we created on top of the level so it will show through ' Delay(30) Flip ' Show what we've got Until d <= 10 ' Loop again when we've got a really small circle Forever End Function Let it run for half a minute or so and it will bring your entire system to a screeching halt (even after you stop it), requiring a restart. Also, since the mask color is 255,0,255 (same as the color of the circle), shouldn't it show the items behind it through? What am I missing here? Thanks, Russell p.s. Even with DrawLevel() and Delay() commented out, it still crawls... |
| ||
Hello. Just a guess (at work, see) but it might be that the grabimage(circle,0,0) is grabbing the image into memory and not freeing the previous grabimage? Perhaps you have to free the circle image prior to calling grabimage again? I dunno, just a thought. Goodbye. |
| ||
Are you're trying to do a special effect like fade out?. grabbing the screen isn't the way to do it. |
| ||
There's definitely something not getting freed up -- if I run the program in windowed mode and look at the memory usage in task manager, it starts out at 400Mb in use, rapidly climbs to ~1GB, then drops to ~400MB, climbs back up -- rince and repeat. Depending on how much memory your machine has, there's a good chance that you are running out of available physical memory and your computer starts hammering the much slower swap file to be able to accomodate all the data. |
| ||
A quick test by putting gccollect() before the grabimage statement seems much more fluid, with total memory usage staying stable in the ~400MB range on my machine, without the rapid spikes upwards. Not sure what the issue is that prevents the automatic garbage collection from working properly, although it might just be the insane amounts of data you are pumping around between the automated cleanup cycles. (Grabbing a full screen each frame really adds up very quickly) |
| ||
There are a few things I should point out, You are copying a 800x600 image 1000 times in a loop. You are then drawing that image that was grabbed 1000 times. Total of 2000 grab/draw operations on a 800x600 image. The example in the code archives isnt a good example as it isnt realtime. You even notice the lag as it draws the seccond set of circles. |
| ||
Stab in the dark. Flushmem used to work from a mainloop. If you had seperate long-running functions you were recommended to add another flushmem. In this example if you add a print gcmemalloced() gccollect() after the flip the memory leak does not occur. Maybe there's the same restriction ? |
| ||
use it, is a better way to do what you want.Strict Graphics 800,600,0 SetMaskColor 255,0,255 Local circle:TImage=CreateImage(256,256) MidHandleImage(circle) SetColor 255,255,255 DrawRect 0,0,circle.Width,circle.Height SetColor 255,0,255 For Local r:Int=0 Until circle.Width DrawOval((circle.Width-r)*0.5,(circle.Height-r)*0.5,r,r) Next GrabImage circle,0,0 SetClsColor 128,128,128 Local centerx:Int=GraphicsWidth()*0.5 Local centery:Int=GraphicsHeight()*0.5 Local scale:Float=4 Local fadeout=True While Not KeyHit(KEY_ESCAPE) Cls 'draw the game graphics SetColor Rand(255),Rand(255),Rand(255) DrawRect Rand(800),Rand(600),100,100 'draw circle SetScale scale,scale SetColor 0,0,0 'circle color DrawImage circle,centerx,centery SetScale 1,1 DrawRect 0,centery-(circle.Height*0.5)*scale, GraphicsWidth(),-centery DrawRect 0,centery+(circle.Height*0.5)*scale, GraphicsWidth(),centery DrawRect centerx-(circle.Width*0.5)*scale,0, -centerx,GraphicsHeight() DrawRect centerx+(circle.Width*0.5)*scale,0, centerx,GraphicsHeight() SetColor 255,255,255 Flip If fadeout Then scale:-0.05 If scale<0 Then fadeout=False Else scale:+0.05 If scale>4 Then fadeout=True EndIf Wend |
| ||
If you create an image or you grab one, it is my understanding that it creates a new image, it doesn't grab it `into` an existing image. But I might be wrong. If that is how it works, then allocating a new image each time is going to fill up all your video memory and then the driver is going to spool everything off into main memory to backup the textures, which may turn into virtual memory, and is going to hugely slow things down, and additionally those textures then have to be spooled back in FROM memory (i think) in order to be deallocated, hence all the extra data crunching going on when you've already exited due to the system trying to free up the memory. You must free the image after you have done using it before you grab again. - Technically that is not the most efficient because it is faster to grab into an existing image than to grab a whole new image because then the video ram has to be reallocated and deallocated which slows things down. e.g. in OpenGL glCopyTexSubImage2d() which copies into an existing texture is the fastest way to grab an image. |
| ||
I tried several ways to free up the image, such as circle = Null , etc but nothing seemed to have a positive impact on the problem. And there doesn't seem to be a 'freeimage()' function (?) The thing is, though, I've done this with Blitz 2D and it worked fine, although in that case I used the SetBuffer(ImageBuffer()) methodology. Anyway, I guessed that the memory wasn't being freed up, even though it should have been when circle gets used again, right? What I was shooting for is a camera Iris screen transition. I'll give BlackSp1der's a try. Thanks again, Russell |
| ||
p.s. BlackSp1der: I just tried it: Thanks! That's exactly what I'm looking for! (Though a bit slower ;) Russell |
| ||
You have to do =Null AND then do freemem or flushmem or whatever |
| ||
Ah! Well that explains it ;) Although I thought automatic garbage collection cleans it up itself if it equals Null? I guess GC couldn't keep up in this case maybe? (Too bad the old image isn't automagically freed when a new image is assigned to its 'handle', eh?) I guess using Basic for so long has made me lazy... Russell p.s. BlackSp1der, I just realized that in my post reply when I say 'Though a bit slower' it sounds like I'm saying your function is slow. No, I mean I need it to be a bit slower: It's too fast! I can adjust the speed, no problem. Thanks again. |
| ||
@BlackSp1der: Thanks again for your code snippet. I was trying to figure out what you were doing in the top part of the program: Strict Graphics 800,600,0 SetMaskColor 255,0,255 Local circle:TImage=CreateImage(256,256) MidHandleImage(circle) SetColor 255,255,255 DrawRect 0,0,circle.Width,circle.Height SetColor 255,0,255 For Local r:Int=0 Until circle.Width DrawOval((circle.Width-r)*0.5,(circle.Height-r)*0.5,r,r) Next GrabImage circle,0,0 and discovered that it runs excatly the same way if you change it to: Strict Graphics 800,600,0 SetMaskColor 255,0,255 Local circle:TImage=CreateImage(256,256) MidHandleImage(circle) SetColor 255,0,255 DrawOval(0,0,256,256) GrabImage circle,0,0 What was the For/Next loop doing? And what do we need the rectangle for? I'm surprised that the circle is transparent in the center since the BMax constant MASKEDIMAGE wasn't used when creating circle:TImage Looks pretty smooth, even though MIPMAPPEDIMAGE wasn't used, too. Thanks again. Russell |
| ||
@Rusell: What was the For/Next loop doing? And what do we need the rectangle for? sorry, I thought that DRAWOVAL draws only the circumference. You can use only one drawoval. Drawrect is used to have the circle outside in white. Since the circle outside is white, you can change the fadeout color with setcolor SetColor 0,0,0 'circle color try other color DrawImage circle,centerx,centery |