Loader profiler for Irrlicht

To use it, just create an instance of it and call attach(), it will be added as a mesh, image, scene and archive loader that intercepts all load calls, passes them to the correct loader while timing them, then outputs the result.

If nothing else, it’s a good example of how to override a built-in loader with your own :)

// Irrlicht loader profiler by gaz@bitplane.net
// requires Irrlicht 1.8 or above.


#include "IImageLoader.h"
#include "IMeshLoader.h"
#include "ISceneLoader.h"
#include "IFileArchive.h"
#include "path.h"

namespace irr 
{

// When added to Irrlicht as an external loader or writer, this class claims intercepts all calls 
// and puts timings around them.
class LoaderProfiler : public virtual video::IImageLoader, scene::IMeshLoader, scene::ISceneLoader, io::IArchiveLoader
{
	public: 

	LoaderProfiler(IrrlichtDevice* device=0) : Device(device)
	{
	}

	void attach() 
	{
		// register the interceptor.
		if (Device)
		{
			Device->getVideoDriver()->addExternalImageLoader(this);
			Device->getSceneManager()->addExternalMeshLoader(this);
			Device->getSceneManager()->addExternalSceneLoader(this);
			Device->getFileSystem()->addArchiveLoader(this);
		}
	}

	//! Used by all the loaders except archives, where this method is badly named.
	virtual bool isALoadableFileExtension(const io::path& filename) const 
	{
		// since we have no idea what it is we're loading, we have to check all loaders/writers.
		// since C++ sucks, we have to write this crap out many times.

		scene::ISceneManager* smgr  = Device->getSceneManager();
		video::IVideoDriver*  video = Device->getVideoDriver();

		s32 i;

		// image loaders
		i = video->getImageLoaderCount()-1;
		for (;i>=0; --i)
			if (video->getImageLoader(i) != this && 
			    video->getImageLoader(i)->isALoadableFileExtension(filename))
				return true;

		// mesh loaders
		i = smgr->getMeshLoaderCount()-1;
		for (;i>=0; --i)
			if (smgr->getMeshLoader(i) != this && 
			    smgr->getMeshLoader(i)->isALoadableFileExtension(filename))
				return true;

		// scene loaders
		i = smgr->getSceneLoaderCount()-1;
		for (;i>=0; --i)
			if (smgr->getSceneLoader(i) != this && 
			    smgr->getSceneLoader(i)->isALoadableFileExtension(filename))
				return true;

		// no match
		return false;
	}

	//! badly named! need to fix this at some point
	virtual bool isALoadableFileFormat(const io::path& filename) const 
	{
		io::IFileSystem* fs = Device->getFileSystem();
		// archive loaders
		s32 i = fs->getArchiveLoaderCount()-1;
		for (;i>=0; --i)
			if (fs->getArchiveLoader(i) != this && 
			    fs->getArchiveLoader(i)->isALoadableFileFormat(filename))
				return true;

		return false;
	}

	virtual bool isALoadableFileFormat(io::E_FILE_ARCHIVE_TYPE t) const
	{
		io::IFileSystem* fs = Device->getFileSystem();
		// archive loaders
		s32 i = fs->getArchiveLoaderCount()-1;
		for (;i>=0; --i)
			if (fs->getArchiveLoader(i) != this && 
			    fs->getArchiveLoader(i)->isALoadableFileFormat(t))
				return true;

		return false;
	}

	//! Used by all but mesh loaders
	virtual bool isALoadableFileFormat(io::IReadFile* file) const
	{
		// again, C++ sucks.

		scene::ISceneManager* smgr  = Device->getSceneManager();
		video::IVideoDriver*  video = Device->getVideoDriver();
		io::IFileSystem*      fs    = Device->getFileSystem();

		s32 i;

		// image loaders
		i = video->getImageLoaderCount()-1;
		for (;i>=0; --i)
			if (video->getImageLoader(i) != this && 
			    video->getImageLoader(i)->isALoadableFileFormat(file))
				return true;

		// scene loaders
		i = smgr->getSceneLoaderCount()-1;
		for (;i>=0; --i)
			if (smgr->getSceneLoader(i) != this && 
			    smgr->getSceneLoader(i)->isALoadableFileFormat(file))
				return true;

		// archive loaders
		i = fs->getArchiveLoaderCount()-1;
		for (;i>=0; --i)
			if (fs->getArchiveLoader(i) != this && 
			    fs->getArchiveLoader(i)->isALoadableFileFormat(file))
				return true;

		// no match
		return false;
	}


	//! Load an image, time it.
	virtual video::IImage* loadImage(io::IReadFile* file) const 
	{
		video::IImage* ret = 0;

		video::IVideoDriver* video = Device->getVideoDriver();

		u32 readTime=0;

		// find the correct loader
		for (s32 i=video->getImageLoaderCount()-1; !ret && i >= 0; --i)
			if (video->getImageLoader(i) != this &&
			    video->getImageLoader(i)->isALoadableFileFormat(file))
			{
				// attempt to open
				u32 readStart = Device->getTimer()->getRealTime();
				ret = video->getImageLoader(i)->loadImage(file);
				readTime = Device->getTimer()->getRealTime() - readStart;
			}

		if (ret)
			log(readTime, file->getFileName());
		
		return ret;
	}

	//! Load a mesh, time it
	virtual scene::IAnimatedMesh* createMesh(io::IReadFile* file)
	{
		scene::IAnimatedMesh* ret = 0;

		scene::ISceneManager* smgr = Device->getSceneManager();

		u32 readTime=0;

		// find the correct loader
		for (s32 i=smgr->getMeshLoaderCount()-1; !ret && i >= 0; --i)
			if (smgr->getMeshLoader(i) != this &&
			    smgr->getMeshLoader(i)->isALoadableFileExtension(file->getFileName()))
			{
				// attempt to open
				u32 readStart = Device->getTimer()->getRealTime();
				ret = smgr->getMeshLoader(i)->createMesh(file);
				readTime = Device->getTimer()->getRealTime() - readStart;
			}

		if (ret)
			log(readTime, file->getFileName());
		
		return ret;
	}

	//! Load a scene, time it
	virtual bool loadScene(io::IReadFile* file, scene::ISceneUserDataSerializer* userDataSerializer=0,
	                       scene::ISceneNode* rootNode=0) 
	{
		bool ret = false;

		scene::ISceneManager* smgr = Device->getSceneManager();

		u32 readTime=0;

		// find the correct loader
		for (s32 i=smgr->getSceneLoaderCount()-1; !ret && i >= 0; --i)
			if (smgr->getSceneLoader(i) != this &&
			    smgr->getSceneLoader(i)->isALoadableFileFormat(file))
			{
				// attempt to open
				u32 readStart = Device->getTimer()->getRealTime();
				ret = smgr->getSceneLoader(i)->loadScene(file, userDataSerializer, rootNode);
				readTime = Device->getTimer()->getRealTime() - readStart;
			}

		if (ret)
			log(readTime, file->getFileName());
		
		return ret;
	}


	//! Mounting an archive from file name
	virtual io::IFileArchive* createArchive(const io::path& filename, bool ignoreCase, bool ignorePaths) const
	{
		io::IReadFile* f = Device->getFileSystem()->createAndOpenFile(filename);

		io::IFileArchive* ret = createArchive(f, ignoreCase, ignorePaths);

		f->drop();
		
		return ret;
	}

	//! Mount an archive, time it.
	virtual io::IFileArchive* createArchive(io::IReadFile* file, bool ignoreCase, bool ignorePaths) const
	{
		io::IFileArchive* ret = 0;

		io::IFileSystem* fs = Device->getFileSystem();

		u32 start=Device->getTimer()->getRealTime(), readTime=0, totalTime=0;

		// find the correct loader
		for (s32 i=fs->getArchiveLoaderCount()-1; !ret && i >= 0; --i)
			if (fs->getArchiveLoader(i) != this &&
			    fs->getArchiveLoader(i)->isALoadableFileFormat(file))
			{
				// attempt to open
				u32 readStart = Device->getTimer()->getRealTime();
				ret = fs->getArchiveLoader(i)->createArchive(file, ignoreCase, ignorePaths);
				readTime = Device->getTimer()->getRealTime() - readStart;
			}

		totalTime = Device->getTimer()->getRealTime() - start;

		if (ret)
			log(readTime, totalTime, file->getFileName());
		
		return ret;
	}

private:
	IrrlichtDevice* Device;

	void log(u32 readTime, core::stringc filename) const
	{
		core::stringc msg = "LoaderProfiler: took ";
		msg += readTime;
		msg += "ms to load '";
		msg += filename + "'";

		Device->getLogger()->log(msg.c_str());
	}

};

}