Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


#ActualJTippetts

Posted 24 January 2013 - 11:08 PM

What happens if/when your game grows to sufficient size that having all images loaded into statics becomes too much? Typically at this point you might want to look into smarter resource management. You need to implement a better means for obtaining resources (textures, sounds, geometry, etc...) on demand, without requiring them to be in memory all the time. This way, you can load only the resources you need for a particular level or portion of your game, unloading them when they are no longer needed and loading others in their place.

One method for resource management I have seen and used is to implement a pattern where a resource container maintains the resources and hands out shared_ptrs to them upon request. The way this work is that internally, the container implements a map keyed with a string (the name of the resource to load) and with weak_ptr to resource as the value. When a resource is requested, it checks the map first to see if the resource exists in the map (ie, it has been loaded at one point). This check merely indexes the map to see if there is an entry for the resource. If there is an entry, then it next checks the weak_ptr to see if it is valid. If it is not valid, that means the shared_ptrs that the weak_ptr shares with all went out of scope or were otherwise released, and so the resource was unloaded. If the resource is unloaded, or was never loaded to start, then the resource is loaded from file and a weak_ptr to it is stored in the map. Then a shared_ptr is constructed from this weak_ptr and returned for your object to use.

The beauty of this system is that your object doesn't need to explicitly release the resource; it merely has to reset() the shared_ptr, or just let the shared_ptr be deleted when the object is deleted in order for it to go away. A very quick sample implementation:
// spritemanager.h
#include <map>
#include <string>
#include <memory>
#include "Sprite.h"

class SpriteManager
{
	public:
	SpriteManager();
	~SpriteManager();
	
	std::shared_ptr<Sprite> getSprite(std::string name, int w, int h, int unknown1, int unknown2);
	
	private:
	std::map<std::string, std::weak_ptr<Sprite> > sprites_;
};


// spritemanager.cpp
#include "spritemanager.h"

SpriteManager::SpriteManager(){}
SpriteManager::~SpriteManager(){}

std::shared_ptr<Sprite> getSprite(std::string name, int w, int h, int unknown1, int unknown2)
{
	auto iter=sprites_.find(name);
	if(iter==sprites_.end() || (*iter).second.expired())
	{
		auto sprite=std::make_shared<Sprite>(name,w,h,unknown1,unknown2);
		sprites_[name]=std::weak_ptr<Sprite>(sprite);
		return sprite;
	}
	else
	{
		return std::shared_ptr<Sprite>((*iter).second);
	}
}


// Object.h
struct Object
{
public:
	//constructor and deconstructodon Awww yeah!
	Object(/*HWND *hwnd,*/ std::shared_ptr<Sprite> _sprite ,int _x, int _y);
	~Object();
	
	// I forget what these are called, uuuuh like... variables inside a class, it's somethin stupid.
	//HWND hwnd;
	std::shared_ptr<Sprite> sprite;
	unsigned int frame;
	int x, y;
	
	//methods
	void update(/*inputdata*/);
};



//Object.cpp
Object::Object(std::shared_ptr<Sprite> _sprite ,int _x, int _y) :sprite(_sprite), x(_x), y(_y),frame(0)
{
}
Object::~Object()
{
}
void Object::update(/*inputdata*/)
{
}
Then, you would instance SpriteManager and use it to obtain sprites:
smgr=SpriteManager();

Object player(smgr.getSprite("Rainbow.bmp", 64, 64, 4, 4), 10, 10);
At this point, player would have a pointer to the Rainbow.bmp sprite resource. Whenever player is deleted, the stored shared_ptr will be deleted, and the resource dropped. If other objects hold shared_ptrs to the resource, it will not be deleted. But as soon as all shared_ptrs to the resource are deleted, then the resource itself will be deleted and the weak_ptr stored inside SpriteManager will be invalidated. The next time the resource is requested, it will be reloaded.

This is untested code, of course, but it should show the gist of the idea. Using a resource manager, even if you don't follow this specific pattern, is probably a good idea, as it allows you to elegantly load and unload resources as they are used, without having to resort to global variables and other hacks to store your resources.

#1JTippetts

Posted 24 January 2013 - 11:05 PM

What happens if/when your game grows to sufficient size that having all images loaded into statics becomes too much? Typically at this point you might want to look into smarter resource management. You need to implement a better means for obtaining resources (textures, sounds, geometry, etc...) on demand, without requiring them to be in memory all the time. This way, you can load only the resources you need for a particular level or portion of your game, unloading them when they are no longer needed and loading others in their place.

One method for resource management I have seen and used is to implement a pattern where a resource container maintains the resources and hands out shared_ptrs to them upon request. The way this work is that internally, the container implements a map keyed with a string (the name of the resource to load) and with weak_ptr to resource as the value. When a resource is requested, it checks the map first to see if the resource exists in the map (ie, it has been loaded at one point). This check merely indexes the map to see if there is an entry for the resource. If there is an entry, then it next checks the weak_ptr to see if it is valid. If it is not valid, that means the shared_ptrs that the weak_ptr shares with all went out of scope or were otherwise released, and so the resource was unloaded. If the resource is unloaded, or was never loaded to start, then the resource is loaded from file and a weak_ptr to it is stored in the map. Then a shared_ptr is constructed from this weak_ptr and returned for your object to use.

The beauty of this system is that your object doesn't need to explicitly release the resource; it merely has to reset() the shared_ptr, or just let the shared_ptr be deleted when the object is deleted in order for it to go away. A very quick sample implementation:
// spritemanager.h
#include <map>
#include <string>
#include <memory>
#include "Sprite.h"

class SpriteManager
{
	public:
	SpriteManager();
	~SpriteManager();
	
	std::shared_ptr<Sprite> getSprite(std::string name, int w, int h, int unknown1, int unknown2);
	
	private:
	std::map<std::string, std::weak_ptr<Sprite> > sprites_;
};


// spritemanager.cpp
#include "spritemanager.h"

SpriteManager::SpriteManager(){}
SpriteManager::~SpriteManager(){}

std::shared_ptr<Sprite> getSprite(std::string name, int w, int h, int unknown1, int unknown2)
{
	auto iter=sprites_.find(name);
	if(iter==sprites_.end() || (*iter).second.expired())
	{
		auto sprite=std::make_shared<Sprite>(name);
		sprites_[name]=std::weak_ptr<Sprite>(sprite);
		return sprite;
	}
	else
	{
		return std::shared_ptr<Sprite>((*iter).second);
	}
}


// Object.h
struct Object
{
public:
	//constructor and deconstructodon Awww yeah!
	Object(/*HWND *hwnd,*/ std::shared_ptr<Sprite> _sprite ,int _x, int _y);
	~Object();
	
	// I forget what these are called, uuuuh like... variables inside a class, it's somethin stupid.
	//HWND hwnd;
	std::shared_ptr<Sprite> sprite;
	unsigned int frame;
	int x, y;
	
	//methods
	void update(/*inputdata*/);
};



//Object.cpp
Object::Object(std::shared_ptr<Sprite> _sprite ,int _x, int _y) :sprite(_sprite), x(_x), y(_y),frame(0)
{
}
Object::~Object()
{
}
void Object::update(/*inputdata*/)
{
}
Then, you would instance SpriteManager and use it to obtain sprites:
smgr=SpriteManager();

Object player(smgr.getSprite("Rainbow.bmp", 64, 64, 4, 4), 10, 10);
At this point, player would have a pointer to the Rainbow.bmp sprite resource. Whenever player is deleted, the stored shared_ptr will be deleted, and the resource dropped. If other objects hold shared_ptrs to the resource, it will not be deleted. But as soon as all shared_ptrs to the resource are deleted, then the resource itself will be deleted and the weak_ptr stored inside SpriteManager will be invalidated. The next time the resource is requested, it will be reloaded.

This is untested code, of course, but it should show the gist of the idea. Using a resource manager, even if you don't follow this specific pattern, is probably a good idea, as it allows you to elegantly load and unload resources as they are used, without having to resort to global variables and other hacks to store your resources.

PARTNERS