OpenGL vs DirectX Texture Management

BlitzMax Forums/BlitzMax Beginners Area/OpenGL vs DirectX Texture Management

MGE(Posted 2008) [#1]
Dremora posted this a while back:

http://www.blitzbasic.com/Community/posts.php?topic=56916#632770

"You can't control whats in VRAM. If you use DirectX then only the textures are in VRAM that are needed at the moment. Thats managed through DirectX. If you use OpenGL, then you can't do anything about it. This needs the texture in VRAM as OpenGL does not offer its own management (you would need to write an own management)."

But in another post on another forum (check the 2nd post down) the users states OpenGL is handling the textures similar to DirectX.

http://www.gamedev.net/community/forums/topic.asp?topic_id=461133

Anyway, something strange is indeed going on, in this demo I repeatedly load an image into a TImage and display it.

SuperStrict
'SetGraphicsDriver D3D7Max2DDriver()
SetGraphicsDriver GLMax2DDriver()
Graphics(800,600,0,60)
Global t1:TImage
t1 = New TImage
'
While Not KeyDown(KEY_ESCAPE)
 Cls()
 t1= LoadImage("t1.jpg",0) ' any image, but try one 1024x1024
 DrawImage(t1,MouseX(),MouseY())
 Flip(1)
Wend
EndGraphics()
End


In OpenGL mode, memory usage sky rockets climbing to 200-300mb and going back down, etc, etc. Very shaky frame rates, etc, etc.

In DirectX Mode, memory usage seems to be alot less and alot more stable. Frame rate is ofcourse still choppy, but much less noticable compared to OpenGL.

Q) What's the 100% official answer? Is OpenGL mode managing our textures? Swapping in/out as needed?

Q) What internally is happening when we code something like: MyImage:TImage = Null ? Is the internal surface created for that image also being destroyed to free up video memory? If it's not being destroyed, this could be a useful addition to the underlying graphics driver.

Obviously if there is no texture management going on under OpenGL, games that "only" load 32,64,128mb will be ok if they're running on a card that supports their vram requirements. But what if you want to code a game where each level loads a new background, sprites, animations, etc, etc. If nothing is getting destroyed or managed, is it safe to assume eventually performance will suffer?


MGE(Posted 2008) [#2]
This time I monitored my page file useage during the demo. In both cases the starting page file size was 300mb. During the test I watched the page file size grow...

DirectX Mode: Never went over 345mb.
OpenGL Mode: 800mb+ at the point I stopped the app.

So....it would seem that while we only have created 1 TImage in the code, internally, multiple surfaces are being made and "t1" is pointing to the most recent surface created.


MGE(Posted 2008) [#3]
This version gives me white squares in OpenGL mode:
SuperStrict
'SetGraphicsDriver D3D7Max2DDriver()
SetGraphicsDriver GLMax2DDriver()
Graphics(800,600,0,60)
Global t1:TImage
t1 = New TImage
'
While Not KeyDown(KEY_ESCAPE)
 Cls()
 t1= LoadImage("t1.jpg",0) ' any image, but try one 1024x1024
 DrawImage(t1,MouseX(),MouseY())
 Flip(1)
 t1=Null
 GCCollect()
Wend
EndGraphics()
End



MGE(Posted 2008) [#4]
And this one appears to be removing the surfaces once they are not used! Memory usage and page file usage was normal for both DX and OpenGL mode:

Same as above, but I took out the "t1 = null"
SuperStrict
'SetGraphicsDriver D3D7Max2DDriver()
SetGraphicsDriver GLMax2DDriver()
Graphics(800,600,0,60)
Global t1:TImage
t1 = New TImage
'
While Not KeyDown(KEY_ESCAPE)
 Cls()
 t1= LoadImage("t1.jpg",0) ' any image, but try one 1024x1024
 DrawImage(t1,MouseX(),MouseY())
 Flip(1)
 GCCollect()
Wend
EndGraphics()
End



MGE(Posted 2008) [#5]
So...one would have to conclude that as long as you call GCCollect() after loading a new texture into an existing TImage, the previous surface will be destroyed. So in affect, Blitzmax does give us a way to handle texture management. Because otherwise, it appears the old surfaces will hang around till an internal garbage collection occurs.

So if you have 10 backgrounds for your game, you don't have to have 10 different TImage's, you can use the same one and load a different url when needed, just call GCCollect() after you load the new image and the old surface should be destroyed.

Unless....I still have this all wrong. Need a guru to set me straight! :)


Grey Alien(Posted 2008) [#6]
I'm pretty sure with a normal game that would not have such a tight little loop, that GC would occur naturally after a few cycles of logic and draw anyway.


MGE(Posted 2008) [#7]
Ofcourse.... what I was trying to figure out is:

a) Does OpenGL mode have managed texture resource handling.

b) What effects would it have on a game if there was no texture management in OpenGL mode.

c) If internal surfaces were destroyed, or do they just hang around taking up valuable vram resources? In the demos above, the TImage resource keeps on taking up more and more vram / sram. In the last example, if a garbage collection is forced it appears the surface is released.

It would be nice if we knew exactly how it performs under the hood. Because you really should only be using as many Timages as you need per level. Then you should be able to reload new data into those Timages as needed.


ImaginaryHuman(Posted 2008) [#8]
Until the garbage collector gets called, any previous overwritten references to images basically still exist, meaning that the images are still textures/surfaces in video ram. If you do GCCollect you are flushing the references and therefore deallocating any memory for objects which those references used - ie deleting textures from video ram. It's probably wise to force the GCCollect within the loop.

From what I remember, OpenGL has a system whereby you can say that textures have a priority level, and a state of whether the texture is `resident` ie stored in video ram. When you use too much space, it moves texture data to main memory temporarily. When you then need to use the texture to draw something it moves it back into video ram, perhaps forcing some other texture to be moved out. Moving stuff out/from to main memory majorly slows things down like doing a pixmap transfer.

OpenGL does handle that for you, from what I understood. That's why it gives you the commands to prioritize and manage residency of the textures. But blitzmax doesnt have commands to do that as such - you gotta make direct GL calls. If you don't make those calls, GL will decide for you which textures to move out as needed. Possibly DX has some kind of limit on how much virtual texture storage it can use before capping it and losing the data? I was playing with a GL texture thing a while ago and accidentally was creating new textures every frame, and after several frames I suddenly got a massive slowdown as it started to spool to ram. It didn't cause any errors in the execution of the program, suggesting the images still can be used, just slower.

So if you don't explicitly either detect and manage the amount of VRAM you use ie load no more texture/screen data than there is video ram for, and you don't destroy unused textures along the way, you are going to fill up video ram and overflow it into main memory and will experience slowdown (similar to when Windows switches to virtual memory on slower machines).

a) Yes
b) Slowdown when you store more than there is memory for
c) Resources are not destroyed until you garbage collect or explicitly remove the image


Grey Alien(Posted 2008) [#9]
I basically load in the images I need for a screen and null them all when the screen is destroyed, this frees up the VRAM for the next screen. I don't explicitly call GC, you shouldn't need to as it'll happen after a few normal cycles anyway (or maybe less than that even). Also I was careful in Fairway to make sure I only loaded about 32MB of textures into VRAM at any time to avoid texture swapping on low memory cards.


MGE(Posted 2008) [#10]
Thanks for the comments. Good info. :) Well, as shown by my first example, garbage collection isn't happening at all. So it does seem needed to force GC at the least in between levels after you have nullified TImage references. Would still make more sense if the internal surface handling would free up the resources when the TImage is Nullified.


dmaz(Posted 2008) [#11]
Well, as shown by my first example, garbage collection isn't happening at all.
I don't believe your first example shows that at all. normal garbage collection doesn't happen every vblank... your first test on my machine behaves exactly as I expected, garbage collection happens it seems every couple of seconds. using a picture of 1024x1024 is too big for such a test...

SuperStrict
'SetGraphicsDriver D3D7Max2DDriver()
SetGraphicsDriver GLMax2DDriver()
Graphics(800,600,0,60)
Global t1:TImage
t1 = New TImage
'
While Not KeyDown(KEY_ESCAPE)
 Cls()
 t1= LoadImage("11931924G.jpeg",0) ' any image, but try one 1024x1024
 DrawImage(t1,20,30)
 Echo()
 Flip(1)
Wend
EndGraphics()
End

Function Echo()
	Global peakmem:Int

	Local currentmem:Int = GCMemAlloced()
	If currentmem > peakmem Then peakmem = currentmem

	SetColor(255,255,255)
	
	DrawText currentmem+" -> "+peakmem,10,10

End Function



MGE(Posted 2008) [#12]
Dmaz - Your example gives me the same results as my first one. My page file size grows and grows, never shrinks. However when I use a smaller texture (256x256) instead of 1024x1024, resources were free'd up better and the page file size behaved normally. I'm sure behavior will fluctuate based on cpu, gpu abilities.

The test was originally done to see how OpenGL, DX reacts to VRam being over flooded. There's been a couple of posts on this forum stating OpenGL doesn't have DirectX like texture management, so I wanted to see how both react when Vram is flooded.

I'm still confused becuase IH's comments tate there is texture management under OpenGL but things will slow down if you try to store more than what the card can hold. What's confusing is, "store" is different from "render".

With DirectX you can safely take this approach: (If you want to.)

Load ALL of your graphics media at program start into TImages. This way you have "stored" all of your graphics into sram. And then when your program uses one of these Timages in your game loop, they are first moved into vram and rendered onto the screen. As long as the total space used by the "active timages in your render loop" is not greater than the space available in the graphics card, there should be no slow down. Unless you're rendering too many sprites, particles, etc, resulting in over draw. (Granted you'll get an initial stutter as it moves from sram into vram.)

Will OpenGL behave the same way?


tonyg(Posted 2008) [#13]
By default, most OpenGL drivers will use an LRU (Least Recently Used) algorithm to swap textures.
taken from Understanding and Using OpenGL Texture Objects
So, if this was me asking the question, I would be happy that Mark's implementation of DX driver uses the Automatic Texture Management DX provides and that OGL uses a similar LRU algo.
Anyway, other than getting a response from Mark and/or posting on the API forums you might want to do a search for Video Memory monitors and see what happens.


MGE(Posted 2008) [#14]
In the d3d7max2d.bmx driver I can clearly see that the use managed texture flags are being set.

"desc.ddsCaps2=DDSCAPS2_TEXTUREMANAGE"

But I don't know enough about OpenGL to look through the source to see what's happening during texture creation.


tonyg(Posted 2008) [#15]
I don't know OGL very well either. However, when I looked through the code last year I convinced myself DX driver uses Auto Texture Management and I couldn't see an equivalent for OGL. I wasn't that fussed with OGL at the time but, if I HAD been, I would have concluded it either doesn't do texture management or does it by default. I would then have searched (probably gamedev.net first) and found the OGL article on Gamasutra. I would then have convinced myself OGL does it by default and left it at that.
***IF*** I was still not happy with the response I would have posted exactly as you have and asked for feedback.

Here is the results from my T61 (372.23mb VRAM)
Test1 : OGL stabilises at 100mb VRAM usage.
DX uses 356Mb of VRAM.
In both cases the image disappears when I move the mouse.
Test2 : OGL uses 6Mb of VRAM and displays white rectangle.
DX used 52Mb of VRAM and image disappears when mouse it moved.
Test3 : OGL uses 12mb of VRAM and image disappears.
DX uses 52Mb of VRAM and image disappears.
If I was testing this in anger I would check my drivers and/or conclude that my Graphics card is rubbish.


ImaginaryHuman(Posted 2008) [#16]
I took a look at the Blitz sourcecode. From Blitz's viewpoint, it has to at some point upload a pixmap to a texture before you can draw with it. That only has to happen once for multiple draws. I did not find anything in the source that looks like it's managing how many textures are in use or anything like that.

Like I said before, OpenGL itself manages the texture and moves them from video ram to main memory by itself as needed when there isn't enough space.