Draw-program : optimize?

BlitzMax Forums/BlitzMax Beginners Area/Draw-program : optimize?

Jean-Marie(Posted 2009) [#1]
Hi,

As first steps I wrote a small program to draw with the mouse. It works, but I find it runs quite slow, cpu-intensiv.

The drawing is far from fluent.

Under Ubuntu 9.04, compiled in release mode:
Process requires 40% of CPU on Ubuntu 9.04, intel T3400@....
Inspecting memory maps for the process reveals an increasing number of "/drm mm object (deleted)" of size 2MiB, and the "[heap]" seems to constantly increase.

Under Windows XP: Process requires 100% of CPU.
While drawing, memory usage varies between 5 and 10K. While idle or simply moving mouse, it goes up to 400K, and down again while drawing.

I stripped down my code to the following, which compiles and has the same symptoms:

Const WindowWidth:Int = 640
Const WindowHeight:Int = 480

Graphics WindowWidth , WindowHeight , 0

Local theImage:TImage = CreateImage:TImage( WindowWidth , WindowHeight , 1 , DYNAMICIMAGE )

Cls

LockImage( theImage:TImage )
GrabImage( theImage:TImage, 0, 0 )
UnlockImage( theImage:TImage )

Local newX:Int , newY:Int , lastX:Int , lastY:Int
Local displayRequired:Int = true

Repeat
	
	WaitEvent()

	newX = MouseX()
	newY = MouseY()
	
	If ( KeyDown( KEY_D ) )
	
		DrawImage( theImage:TImage, 0, 0 )
		SetLineWidth( 5 )
		SetColor( 255 , 255 , 255 )
		DrawLine( lastX , lastY , newX , newY )
		displayRequired = True
		
	EndIf

	lastX = newX
	lastY = newY
	
	If ( displayRequired )
	
		LockImage( theImage:TImage )
		GrabImage( theImage:TImage, 0, 0 )
		UnlockImage( theImage:TImage )
	
		DrawText( CurrentMode , 0 , 0 )

		Flip
		
	EndIf

Until KeyHit( KEY_ESCAPE )


Questions:

1. My main concern: is there a better way than GrabImage - DrawImage ? How cost-intensiv is this? More generally: how can I optimize the code above?

2. My curiosity: am I wrongly interpreting the memory maps? Is this a case of garbage collection which was not yet triggered?


_Skully(Posted 2009) [#2]
First point is that when you call lockimage you are telling the system that it needs to re-upload the pixelmap on the next flip.. no need to lock the image when using grabimage... that's your first hit

Also, unlockimage is not necessary.. its taken care of at flip time automatically and the command currently does nothing.

The second hit is the drawtext...

put
SetImageFont Null

at the top of your program somewhere... I read somewhere that speeds drawtext up... otherwise its slower than molasses in January.


Ked(Posted 2009) [#3]
Alright. First off, why do you have WaitEvent() in there?! You are using PolledInput commands, so WaitEvent() is not necessary.

Second, GrabImage/GrabPixmap is a horrible command for real-time use. BlitzMax doesn't carry any real buffer commands, so you are kind of at a loss on this one. (Note: This is your slowdown.)

Third, LockImage and UnlockImage is also not necessary when using GrabImage. (As _Skully says.)

@_Skully: DrawText doesn't have any slowdowns that I'm aware of. DrawText, unlike GrabImage and GrabPixmap, is allowed and OK for real-time use. When I timed how long this function took in the above example, I kept getting 0-1 millisecs. (Which is really good.)

Also, SetImageFont Null only reverts the set imagefont to the default BlitzBasic font. And since the above example is using the default BlitzBasic font, its like Jean-Marie has called SetImageFont Null anyway!


Jean-Marie(Posted 2009) [#4]
Ok, thanks for the replies.

I used the GrabImage/DrawImage because I wanted to do interactive drawing and have overlay graphics - e.g. the current drawing mode. So the loop was

repeat
draw image
draw user action
grab image
draw overlay
until...

Do you know another way to do this, without the penalty of GrabImage?

@Ked: how do you measure the time for the DrawText? are there some profiling Functions or tools?

Jean-Marie.


Brucey(Posted 2009) [#5]
DrawText doesn't have any slowdowns that I'm aware of

DrawText is quite inefficient.
It's fine if you only have to draw a few bits of text each frame, but after that it will start to be a burden. If you only have a few strings to draw, then sure, use DrawText.

Otherwise, you should seriously consider using a "single surface" based text drawing routine, where all the letters come from a single texture. This is far more efficient - by an order of magnitude.

Anyway, in the above case of a single DrawText, it is not an issue here.
I would, however, expect the GrabImage/DrawImage cycle to be much more of a problem. Copy all the data from the GFX card... copy it all back again... etc.


matibee(Posted 2009) [#6]
Another way..

Draw to the screen without clearing it, and each time the mouse is released grab the screen as an undo buffer...



A bit hacky but you should get the idea.

Cheers
Matt


ImaginaryHuman(Posted 2009) [#7]
That won't work, not all drivers preserve the contents of the backbuffer when the display flips, it can be totally trashed. Also OpenGL states that the contents of the backbuffer is totally unpredictable after a flip.


_Skully(Posted 2009) [#8]
I suggest you draw to the pixelmap, since you don't have 1000 things going on at once this should suffice... use lockimage to get the pixelmap... draw to it and then only flip at specific intervals (using a timer)


matibee(Posted 2009) [#9]
That won't work, not all drivers preserve the contents of the backbuffer when the display flips, it can be totally trashed. Also OpenGL states that the contents of the backbuffer is totally unpredictable after a flip.


mmm I didn't know that, thanks. Still it's close, we should just have to draw to a pixmap instead of the backbuffer as Skully suggests. The render loop would be then;

cls
draw last image in the undo buffer
draw the "drawing to" pixmap
flip


Jean-Marie(Posted 2009) [#10]
Hi,

thanks for the tips. So I changed as proposed (at least, as I understood :-) ):
Create a pixmap, clear it
Repeat
   Depending on mouse move, use WritePixel( pixmap... )
   cls
   drawpixmap
   drawtext
   flip
until keyhit


First, I've lost the higher level DrawLine() and SetLineWidth(). I used code from codearcs instead ( http://www.blitzbasic.com/codearcs/codearcs.php?code=1465 ). Not so pretty at this time, but does the trick.

Second, and more important for me: resources.
I start with a 4MB process. Touching the window let it grow to 40 MB. A few mouse moves over the window, and that make a 600 MB process, that don't seem to go down (tried to wait for some garbage collection).
And the process runs at full 50% (i.e. one proc full)

How can I get control over this?


_Skully(Posted 2009) [#11]
use a timer to free up cycles.... full speed you are probably writing to the same pixels over and over


Jean-Marie(Posted 2009) [#12]
I made some tries for managing the processor resources. The one with the timer worked, but somehow, I don't like the idea of repainting when not needed. So I made a try with WaitEvent(), which looks not bad at all - more questions in another post ( http://www.blitzbasic.com/Community/posts.php?topic=86278 ).

Now... about the memory. Can someone give me an explanation, why my small process consumes 600 MB after a short time? Or am i misreading the system monitor infos?

And then: can I do something about it? I already tried to add GCCollect() in my main loop, but it had only very little effect.


MGE(Posted 2009) [#13]
flip(1), flip(1),flip(1),flip(1), flip(1),flip(1),flip(1), flip(1),flip(1),flip(1), flip(1),flip(1).


Jean-Marie(Posted 2009) [#14]
The flip(1) did not help.

On Windows, with the same code, the memory oscillates around 16MB. So this seems to be Linux/Ubuntu relative. I also saw some bug reports about memory leaks in xfree.

So, probably nothing to do with BlitzMax.