switching gfx mode crashes bmx

Archives Forums/BlitzMax Bug Reports/switching gfx mode crashes bmx

anawiki(Posted 2010) [#1]
Hi
I got a report from portals that switching full screen / windowed mode in my game crashes it. It looks like the issue is running out of RAM / VRAM when Graphics command is called multiple times. Piece of code:

Strict

'Import odd.aspect

Global m, i

Global img:TImage[100]

For i=0 To 99
	img[i] = LoadImage(CreatePixmap(800, 600, PF_RGBA8888))
Next

i=0
While Not KeyHit(KEY_ESCAPE)

	Print i
	Graphics 800, 600', m*32
'	SetAspectResolution 800, 600, ASPECT_LETTERBOX_BORDER
	
	DrawImage img[i Mod 99], 0, 0
	Flip
	m = 1-m
	i:+1

Wend



This code just creates new windowed mode every flip because it's easier to watch Task Manager when the screen doesn't flicker, but it doesn't matter if you make it go back and forth.

The number of times you have to recreate Graphics depends on your video card. If you have something powerful it will take longer, if you are on netbook or on awesome integrated card it will happen sooner.

In my case sample program crashes on 104 iteration. On my friends comp it crashes on 156. Of course no one will switch modes that many times, but in my game that is quite art heavy it is enough to switch 5 times to crash it on Samsung NC10 (and memory usage before starting switching modes is around 300MB).

Memory usage is bigger with every image drawn (which is natural), but also with every call to Graphics (in my game each call added ~10MB mem usage and GCCollect() before calling Graphics cleared only few bytes).

If you replace image with something smaller like 32x32 pixmap you still can notice memory leak.

Tested on BMX 1.39 and BMX 1.41.


GfK(Posted 2010) [#2]
When you say "it crashes" - how??

I ran it twice - the first time the screen froze on iteration 49 and I couldn't even start task manager.

The second time it did the same on #46 and upon rebooting my Chrome config file was corrupted... so I am a little reluctant to run it any more.

However, two notes from your sample code. First, you aren't using EndGraphics before creating a new graphics context. Second, your program just blats away trying to suck up as much resources as it can, as quick as it can. Really, GC isn't getting a look-in.

I'm not saying there's no issue... just perhaps need a more reliable/realistic method of demonstrating it.


ima747(Posted 2010) [#3]
Bigest problem as GfK mentioned is you're not ending graphics before creating a new context. You shouldn't technically HAVE to I think... but endgraphics would certainly help.

Additionally you could be noticing a garbage collector failure like I've reported at http://www.blitzbasic.com/Community/posts.php?topic=91511


anawiki(Posted 2010) [#4]
EndGraphics doesn't help you, because it is built in Graphics command - check the sources for Graphics command.

When it crashes it means that I receive debugger message saying "Unhandled Exception: Attempt to access field or method of Null object". In my code it points to DrawImage, but generally this is the first instruction that needs to use Graphics context.

My system seems to be more stable than yours GfK ;-)


SLotman(Posted 2010) [#5]
Why are you creating 100 images of 800x600?

You specify each as 32 bit images, which is 4 bytes per pixel. 800x600x4 is roughly 2Mb per image. So you're creating something like 200Mb worth of images on memory!

I guess the problem is not on Graphics... but on Drawimage. just try:

if (img[i mod 99]) then DrawImage img[i Mod 99], 0, 0


To avoid drawing null images that couldn't be created due to lack of memory.

EDIT: just tested it here on BMAX 1.28 - no problem whatsoever using my null image test.


Zeke(Posted 2010) [#6]
problem is DX9 driver
use
SetGraphicsDriver GLMax2DDriver()

'or

SetGraphicsDriver D3D7Max2DDriver()



xlsior(Posted 2010) [#7]
You specify each as 32 bit images, which is 4 bytes per pixel. 800x600x4 is roughly 2Mb per image. So you're creating something like 200Mb worth of images on memory!


Actually, they are stored in nearest-power-of-two format, so 800x600 actually takes up 1024 x1024 x 4 bytes = 4MB per image. At 100 images, that's 400 MB.


anawiki(Posted 2010) [#8]
@SLotman: I use 100 images because I need VRAM to be staffed with images, otherwise it will take forever to crash the program. The thing is that each Graphics command eats extra RAM/VRAM and this crashes program.

I can't use (if null then don't draw) - it's a game - if user switches to Window and random image gets out of memory what do you expect the game to do - reload? That would be great, if there was free RAM to load it into.

If DX9 is the problem then I kindly ask to fix it :) I can't use OpenGL (portals don't like it) and DX7 might be ugly on Vista/7 (unless I'm wrong) - that's the reason why DX9 is default driver on those systems, isn't it?


Zeke(Posted 2010) [#9]
Hi, i found that bug and fixed it (took whole day to find it). There is memory leak because D3D9Textures are never released when use EndGraphics.

here is quick fix:

dxgraphics.mod/d3d9graphics.bmx add:
import brl.linkedlist

Global textures:tlist=new tlist

Type TD3D9Texture
	Field tex:IDirect3DTexture9
End Type


and same file type TD3D9Graphics ,Close method:
Method Close()
	If Not _hwnd Return

	'Release All Textures
	For Local tex:TD3D9Texture = EachIn textures
		tex.tex.Release_
	Next
	textures.clear()
		
	CloseD3DDevice
	If Not _attached
		DestroyWindow( _hwnd )
	EndIf
	_hwnd = 0
End Method


and same file type TD3D9GraphicsDriver add:
Function DestroyTexture(tex:IDirect3DTexture9)
	For Local t:TD3D9Texture = EachIn textures
		If t.tex<>tex Continue
		textures.remove(t)
		Return
	next	
End Function

Function SaveTexture(tex:IDirect3DTexture9)
	Local t:TD3D9Texture = New TD3D9Texture
	t.tex=tex
	textures.addlast(t)
End Function


and file D3D9Max2D.mod/D3D9Max2D.bmx type TD3D9ImageFrame, Delete method:
Method Delete()
	If _texture
		If _seq=GraphicsSeq
			If _texture=_bound_texture
				_d3dDev.SetTexture 0,Null
				_bound_texture = Null
			EndIf
			TD3D9GraphicsDriver.destroyTexture(_texture) 'Add This (delete texture from textures list)
			_texture.Release_
		EndIf
		_texture = Null
	EndIf
End Method


and Create method:
If _d3dDev.CreateTexture( pow2width,pow2height,levels,usage,format,pool,_texture,Null )<0
	d3derr "Unable to create texture~n"
	Return
EndIf
TD3D9GraphicsDriver.SaveTexture(_texture) 'Add this line


Im now testing first post code with above fixes, and counter is now over 700, but memory usage is still 200-300Mb.


anawiki(Posted 2010) [#10]
Zeke: you are awesome! I will test it on Monday and report how it works for me.


anawiki(Posted 2010) [#11]
Hey Zeke, it all works great, both for the game and the sample code above. Thanks for great work!


GfK(Posted 2010) [#12]
Should this be included in the next Blitzmax update?


anawiki(Posted 2010) [#13]
I think so.


Zeke(Posted 2010) [#14]
yes. this was just quick copy from dx7 version. (but im using global textures list.. and this may be causing problems if multiple graphics windows is open.. (or atleast slowdown because all textures must be load again, if there is any endgraphics command which clear all textures))

Mark should check and fix this.


Zeke(Posted 2010) [#15]
mark check this...


GfK(Posted 2010) [#16]
bumpety bump....

:)


marksibly(Posted 2010) [#17]
Hi,

Will fix - thanks Zeke!


therevills(Posted 2010) [#18]
Will fix - thanks Zeke!


Hi Mark, any ETA for the fix in the official build?

If not, are you just using Zeke's fix?

Thanks!


anawiki(Posted 2010) [#19]
I'm using Zeke's code for now.


Grafos(Posted 2010) [#20]
Thanks Zeke, this worked a treat!


GfK(Posted 2011) [#21]
I know Mark's busy monkeying about, but any ETA yet on how soon a new version will be with this fix included? Just getting back into coding after a lil break and going through and updating everything to latest versions.

Oh - I just added Zeke's fix (above) and noticed a problem. The D3D9 module would no longer compile as it said destroyTexture() wasn't defined. Where it says to add two functions, shouldn't they both be methods? I changed them to methods and now it compiles OK.

Last edited 2011


GfK(Posted 2011) [#22]
Actually something's not right.

This code causes a MAV (even in Debug mode) with no specific error, when End is called.
Graphics 800,600

Local img:TImage = CreateImage(256,256)

SetBlend solidblend
SetClsColor 255,0,0

While Not KeyDown(key_escape)
	Cls
	DrawImage img,Rand(0,800),Rand(0,600)
	Flip
Wend

End



Zeke(Posted 2011) [#23]
boom boom boom boom. i want this thread in your room(to Mark)... ^^ GfK your code works fine here.


therevills(Posted 2011) [#24]
Just tested the original code by anawiki using v1.42:

Memory never went over 300MB in Debug Mode and never over 260MB in Release Mode.... I left it going for over 1300 .

Looks good to me :)


Pengwin(Posted 2011) [#25]
code works fine here...admittedly memory went up to 400MB, but I left it for over 5000.