LoadImageRef (Loads 1000s of images in a sec).

BlitzMax Forums/BlitzMax Programming/LoadImageRef (Loads 1000s of images in a sec).

Chroma(Posted 2010) [#1]
Here's another part of the Core2D Framework I was working on while deployed.

LoadImageRef replaces the LoadImage command. The first time you load an image it's placed into the TMap. Then every other time you need to reuse that same image you just use LoadImageRef again to get a pointer to that image. I have one identical to this for sounds also. It just makes handling resources more...logical.

If you're working on a sidescroller or shooter, this would help you drastically.

Just replace the "cluster.png" with your own sprite. Let me know what you think.

Here's the code:


Last edited 2010


Zeke(Posted 2010) [#2]
why? global image:timage=loadimage(...) and use that image.
if you need to load same image 1000000 times.. then there is something wrong...

if i have something like:

type TTower
field image:Timage
...
end type

^^ i replace field to global.. and no need to load xxxx times..


ima747(Posted 2010) [#3]
I can see how this could be useful in that the first instance of an image that you request gets loaded during runtime rather than in a bespoke loading area, however Zeke's statement about multiple loadings being inherently wrong is quite true, the problem you reference indicates a failure to manage resources properly in the first place. Additionally loading during game play could cause significant hiccups depending on drive speed, file size, compression level, background tasks, available memory etc. I think you're still better off with traditional loading segment followed by sharing links to the resources in memory as needed.

If this were combined with multithreading to swallow some of the potential hiccup time and a reference counter of some sort so that when the last reference to the image is abandoned it can be released from the manager for the garbage collector to sweep up, it could be very useful for things like maps/backgrounds/etc. for games that want and open world type effect where the game never really has a loading screen, resources just come and go as needed. As is they stack up until you wipe them, and again, there could be quite significant hiccups.


Czar Flavius(Posted 2010) [#4]
I used a similar system for when I had multiple images stored in the same png, and it was slow to use LoadAnimImage on that same file repeatedly, mainly because of png decompression. I stored the pixmaps in the map rather than the actual TImage, so you could create multiple TImages from the same image file.

Last edited 2010


Gabriel(Posted 2010) [#5]
however Zeke's statement about multiple loadings being inherently wrong is quite true, the problem you reference indicates a failure to manage resources properly in the first place.

I completely disagree with this. Zeke's example is fine, as far as it goes, but there are thousands of situations where his example is completely irrelevant. If your game is data-driven, you don't know whether the image can be global or per-instance until the data is loaded. If you're making a library for others to use - or if you want people to be able to add user-created content - you have no idea if they're going to try to load 10 copies of the same image or not. There are many, many reasons why something like this might be absolutely essential. Just because it isn't needed in your project doesn't mean that someone else has a design flaw. They just have different needs.

Now having said all that, this is pretty basic. I'd like to see it handle abstraction of loading from within compressed archives, and for it to manage itself (eg: reference counting, etc, destruction of the image when the last reference has been destroyed, etc) but it's a nice little start.


Czar Flavius(Posted 2010) [#6]
A simpler solution might be to organise image requests into packages. MainGamePackage, Level1Package, Level2Package etc. The user requests MainGamePackage at the beginning. He loads Level1Package for the first level, and at its conlcusion, requests Level1Package to be freed. This would give better control of resources but without making things complicated. It would be handy for most games.


shinkiro1(Posted 2010) [#7]
A few weeks ago I had the exact same idea which looks like this

'------------------------------------------------------------------------------
' Used so images are not loaded twice
' How to use: my_image:TImage = TManagedImage.Load( path )
'------------------------------------------------------------------------------
Type TManagedImage

'------------------------------------------------------------------------------
' This TMap contains all images already loaded
' key: path:String | value: image:TImage
'------------------------------------------------------------------------------	
	Global RessourceMap:TMap = New TMap
	
	
'------------------------------------------------------------------------------
' Lookup in the RessourceMap if the TImage is already loaded
' If loaded     => Return the already loaded Image
' If Not loaded => Return the newly loaded Image
'------------------------------------------------------------------------------
	Function Load:TImage( path:Object, flags:Int = -1 )
		If RessourceMap.Contains( path )
			Return TImage( RessourceMap.ValueForKey( path ) )
		Else
			Local image:TImage = LoadImage( path, flags )
			RessourceMap.Insert( path, image )
			Return image
		EndIf
	EndFunction
	

'------------------------------------------------------------------------------
' Same as above but with AnimImage
'------------------------------------------------------------------------------	
	Function LoadAnim:TImage( path:Object, cell_width:Int, cell_height:Int, first_cell:Int, cell_count:Int, flags:Int = -1 )
		If RessourceMap.Contains( path )
			Return TImage( RessourceMap.ValueForKey( path ) )
		Else
			Local image:TImage = LoadAnimImage( path, cell_width, cell_height, first_cell, cell_count, flags = -1 )
			RessourceMap.Insert( path, image )
			Return image
		EndIf
	EndFunction
	

'------------------------------------------------------------------------------
' Remove the image with the speciefied path
'------------------------------------------------------------------------------
	Function Remove( path:Object )
		If RessourceMap.Contains( path )
			RessourceMap.Remove( path )
			Return
		EndIf
		?debug
		DebugLog( "The image at " + path.ToString() + " could not be removed." )
		?
	EndFunction
	
	
'------------------------------------------------------------------------------
' Clear the RessourceMap of all loaded TImages
' Info: You still have to manually clear all other references to TImages
'       in your own Types
'------------------------------------------------------------------------------	
	Function Reset()
		RessourceMap.Clear()
	EndFunction
	
EndType

For loading you call TManagedImage.Load(..) or TManagedImage.LoadAnim(..)

I posted this because i found it really funny my code was so identical to Chromas ^^


ima747(Posted 2010) [#8]
Re Gabriel: there's always an exception, however the implication I got was that for a shooter you need to load 100 copies of bullet.png for al the bullets on screen, when you should just load one and reuse the resource (as this snippet is actually designed to do for you). My feeling is that if this is a problem for you then you're doing something wrong/inefficient. Again there are always exceptions, I suppose your example of user based content where the user might feed the same picture in for 10 different things is a good example, but realisticlly if someone is using the same picture for 10 things how are they going to tell those 10 things apart (again exception, maybe they don't want to). But if that's the case you've already budgeted that they could load 10 different pictures so the loading waste is still within your resource budget. Additionally you have to purge the lookup list at some point or you will just hemorrhage resources in the future as you never release anything, if you then load something that's still retained somewhere you will get a second copy thus defeating the purpose of caching your resource list...

Nothings ever 100% but I still maintain that if you are relying on this for general resource management (as I felt was implied by the examples given) then it's a solution to a problem that should be optimized out (e.g. Loading a resource every time you create a bullet...)


Czar Flavius(Posted 2010) [#9]
I agree, if you do ANY resource loading during your main game loop, you are 99% of the time doing something wrong (apart from transitions between levels of course, but you get what I mean).

Actually, I have implemented a just-in-time resource loader for my game, but that was more due to laziness than anything else. It's handy for debugging, where I want to spawn a kind of unit that wasn't in the original level list. It won't be relied upon for the final product.


Gabriel(Posted 2010) [#10]
I agree, if you do ANY resource loading during your main game loop, you are 99% of the time doing something wrong (apart from transitions between levels of course, but you get what I mean).

I know Chroma provided a bad example app, but surely no one is actually thinking that this can only be used if you load images in your main loop? Perhaps I've grossly overestimated them, but I never got the impression that ima747 or Zeke were actually thinking that.


Czar Flavius(Posted 2010) [#11]
the implication I got was that for a shooter you need to load 100 copies of bullet.png for al the bullets on screen, when you should just load one and reuse the resource
I've seen beginners trying to do this before, so it's good to clarify for future reference of the thread.


matibee(Posted 2010) [#12]
I didn't want to be seen chiming in just to promote a framework, but since this topic has some debate I'll add my 2pence worth (besides I'm not pimping a product that's actually up for sale)..

I have an instancing manager as part of my framework that ensures any image file is only loaded once and ref-counted, if they're used for sprites or gui elements or anything else. This is only really useful when you're defining resources, for example the gui will allow button state bitmaps to be specified per-button, but I know I can safely refer to "graphics/buttons/bigbutton/over.png" as often as I want in my resource files knowing it'll only ever be loaded once.

I might have a weapons system that defines which bullet bitmap to use for which weapon, but I can specify the same bitmap for multiple weapons and it'll only be loaded once.

I can define a sprite animation as a collection of frames, so walk forwards might specify "frame[1 .. n].png", whereas walk backwards could specify "frame[n .. 1].png", but the individual frames are only loaded once.

Handling 100 bullets (or even 100 animated characters) is different. I have a SpriteList type that will give us as many sprite instances as we need, and each instance has it's own screen position, delta (animation) time, scale, rotation etc. We update the live ones, kill off any we no longer need and spawn new ones. The SpriteList is drawn in one call.

There are examples of this in the framework tutorials and in the samples that come with it. They might give you some food for thought.

Cheers
Matt

Last edited 2010


Czar Flavius(Posted 2010) [#13]
What happens if the number of bullets in the game reaches 0, is the image unloaded? There might be a bullet again next frame.


matibee(Posted 2010) [#14]
What happens if the number of bullets in the game reaches 0, is the image unloaded? There might be a bullet again next frame.


No :P

The SpriteList would be made at the start of the game when all the resources (including the MasterSprite that the SpriteList relies on) are loaded, and nothing would be destroyed until the game ended and all its resources are unloaded. That's just how the resource management works in my framework but your needs may vary.


Chroma(Posted 2010) [#15]
See two posts below.

Last edited 2010


Chroma(Posted 2010) [#16]
See above.

Last edited 2010


Chroma(Posted 2010) [#17]
See below.

Last edited 2010


Chroma(Posted 2010) [#18]
Hmm...just did a test where after creating the 5000 sprites I deleted the image in the TMap and everything still showed. So it's not a pointer or else it would throw an error. So it's copying the image which is the same as loading the same image 5000 times but it's just doing it faster. Looks like I'm going to rewrite this.


Chroma(Posted 2010) [#19]
Re-written to what I actually wanted it to do. Now it uses one image resource.



Last edited 2010


Chroma(Posted 2010) [#20]
Which then morphed into this for use on a global scale.

Btw, I wish I could changed the post title as yes...it is very misleading.

' Core2D :: Global Image Resource Manager (GIRM)

' pronounced 'germ' Lolzzz...
Strict

'Module Core2D.GIRM

'Import BRL.Max2d
'Import BRL.Map

Private

Global _imagemap:TMap = CreateMap()

Public

Global ImageRef:TImage[]

Function LoadImageRef(ImageFile$,midhandle=1,flags=FILTEREDIMAGE|MIPMAPPEDIMAGE)
	If _imagemap.Contains(ImageFile)
		Return Int(String( _imagemap.ValueForKey(ImageFile)))
	Else
		ImageRef :+ [LoadImage(ImageFile,flags)]
		Local ilen = ImageRef.length-1
		If midhandle MidHandleImage ImageRef[ilen]
		_imagemap.Insert(ImageFile,String(ilen))
		Return ilen
	EndIf
End Function

Function ClearImageRefs()
	ImageRef = New TImage[0]
	_imagemap.Clear()
End Function

'Rem
'===TEST===
Graphics 800,600

Local imgID = LoadImageRef("cluster.png")

While Not KeyHit(KEY_ESCAPE)
Cls
	If KeyHit(KEY_SPACE) ClearImageRefs()
	DrawImage ImageRef[imgID],100,100
Flip
Wend
End
'End Rem


Last edited 2010


Chroma(Posted 2010) [#21]
Funny...the program uses the same amount of GCMemAlloced when using either one Global image or if the TSprite has a TImage field and you just return an image already loaded (only once). Is there really any difference? It appears that where the amount of memory is concerned that there isn't.

Last edited 2010