Can someone tell me why this is sooooo slow?

BlitzMax Forums/BlitzMax Programming/Can someone tell me why this is sooooo slow?

Russell(Posted 2006) [#1]
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...


SoggyP(Posted 2006) [#2]
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.


BlackSp1der(Posted 2006) [#3]
Are you're trying to do a special effect like fade out?.
grabbing the screen isn't the way to do it.


xlsior(Posted 2006) [#4]
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.


xlsior(Posted 2006) [#5]
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)


Leiden(Posted 2006) [#6]
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.


tonyg(Posted 2006) [#7]
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 ?


BlackSp1der(Posted 2006) [#8]
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



ImaginaryHuman(Posted 2006) [#9]
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.


Russell(Posted 2006) [#10]
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


Russell(Posted 2006) [#11]
p.s. BlackSp1der: I just tried it: Thanks! That's exactly what I'm looking for! (Though a bit slower ;)

Russell


ImaginaryHuman(Posted 2006) [#12]
You have to do =Null AND then do freemem or flushmem or whatever


Russell(Posted 2006) [#13]
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.


Russell(Posted 2006) [#14]
@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


BlackSp1der(Posted 2006) [#15]
@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