Replaces `Dictionary` with `std::unordered_map`

I did some performance tests, and `std::unordered_map` is more performant than `Dictionary`.

I need to do more tests, but so far, the results are good.

Signed-off-by: Ricardo Quesada <ricardoquesada@gmail.com>
This commit is contained in:
Ricardo Quesada 2013-09-06 22:55:11 -07:00
parent 7d8261c722
commit 487f65af2e
4 changed files with 126 additions and 160 deletions

View File

@ -76,22 +76,22 @@ void ShaderCache::purgeSharedShaderCache()
} }
ShaderCache::ShaderCache() ShaderCache::ShaderCache()
: _programs(0) : _programs()
{ {
} }
ShaderCache::~ShaderCache() ShaderCache::~ShaderCache()
{ {
for( auto it = _programs.begin(); it != _programs.end(); ++it ) {
(it->second)->release();
}
CCLOGINFO("deallocing ShaderCache: %p", this); CCLOGINFO("deallocing ShaderCache: %p", this);
_programs->release();
} }
bool ShaderCache::init() bool ShaderCache::init()
{ {
_programs = Dictionary::create();
_programs->retain();
loadDefaultShaders(); loadDefaultShaders();
return true; return true;
} }
@ -101,70 +101,54 @@ void ShaderCache::loadDefaultShaders()
// Position Texture Color shader // Position Texture Color shader
GLProgram *p = new GLProgram(); GLProgram *p = new GLProgram();
loadDefaultShader(p, kShaderType_PositionTextureColor); loadDefaultShader(p, kShaderType_PositionTextureColor);
_programs.insert( std::make_pair( GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR, p ) );
_programs->setObject(p, GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR);
p->release();
// Position Texture Color alpha test // Position Texture Color alpha test
p = new GLProgram(); p = new GLProgram();
loadDefaultShader(p, kShaderType_PositionTextureColorAlphaTest); loadDefaultShader(p, kShaderType_PositionTextureColorAlphaTest);
_programs.insert( std::make_pair(GLProgram::SHADER_NAME_POSITION_TEXTURE_ALPHA_TEST, p) );
_programs->setObject(p, GLProgram::SHADER_NAME_POSITION_TEXTURE_ALPHA_TEST);
p->release();
// //
// Position, Color shader // Position, Color shader
// //
p = new GLProgram(); p = new GLProgram();
loadDefaultShader(p, kShaderType_PositionColor); loadDefaultShader(p, kShaderType_PositionColor);
_programs.insert( std::make_pair(GLProgram::SHADER_NAME_POSITION_COLOR, p) );
_programs->setObject(p, GLProgram::SHADER_NAME_POSITION_COLOR);
p->release();
// //
// Position Texture shader // Position Texture shader
// //
p = new GLProgram(); p = new GLProgram();
loadDefaultShader(p, kShaderType_PositionTexture); loadDefaultShader(p, kShaderType_PositionTexture);
_programs.insert( std::make_pair( GLProgram::SHADER_NAME_POSITION_TEXTURE, p) );
_programs->setObject(p, GLProgram::SHADER_NAME_POSITION_TEXTURE);
p->release();
// //
// Position, Texture attribs, 1 Color as uniform shader // Position, Texture attribs, 1 Color as uniform shader
// //
p = new GLProgram(); p = new GLProgram();
loadDefaultShader(p, kShaderType_PositionTexture_uColor); loadDefaultShader(p, kShaderType_PositionTexture_uColor);
_programs.insert( std::make_pair( GLProgram::SHADER_NAME_POSITION_TEXTURE_U_COLOR, p) );
_programs->setObject(p ,GLProgram::SHADER_NAME_POSITION_TEXTURE_U_COLOR);
p->release();
// //
// Position Texture A8 Color shader // Position Texture A8 Color shader
// //
p = new GLProgram(); p = new GLProgram();
loadDefaultShader(p, kShaderType_PositionTextureA8Color); loadDefaultShader(p, kShaderType_PositionTextureA8Color);
_programs.insert( std::make_pair(GLProgram::SHADER_NAME_POSITION_TEXTURE_A8_COLOR, p) );
_programs->setObject(p, GLProgram::SHADER_NAME_POSITION_TEXTURE_A8_COLOR);
p->release();
// //
// Position and 1 color passed as a uniform (to simulate glColor4ub ) // Position and 1 color passed as a uniform (to simulate glColor4ub )
// //
p = new GLProgram(); p = new GLProgram();
loadDefaultShader(p, kShaderType_Position_uColor); loadDefaultShader(p, kShaderType_Position_uColor);
_programs.insert( std::make_pair(GLProgram::SHADER_NAME_POSITION_U_COLOR, p) );
_programs->setObject(p, GLProgram::SHADER_NAME_POSITION_U_COLOR);
p->release();
// //
// Position, Legth(TexCoords, Color (used by Draw Node basically ) // Position, Legth(TexCoords, Color (used by Draw Node basically )
// //
p = new GLProgram(); p = new GLProgram();
loadDefaultShader(p, kShaderType_PositionLengthTexureColor); loadDefaultShader(p, kShaderType_PositionLengthTexureColor);
_programs.insert( std::make_pair(GLProgram::SHADER_NAME_POSITION_LENGTH_TEXTURE_COLOR, p) );
_programs->setObject(p, GLProgram::SHADER_NAME_POSITION_LENGTH_TEXTURE_COLOR);
p->release();
} }
void ShaderCache::reloadDefaultShaders() void ShaderCache::reloadDefaultShaders()
@ -297,14 +281,18 @@ void ShaderCache::loadDefaultShader(GLProgram *p, int type)
CHECK_GL_ERROR_DEBUG(); CHECK_GL_ERROR_DEBUG();
} }
GLProgram* ShaderCache::programForKey(const char* key) GLProgram* ShaderCache::programForKey(const std::string &key)
{ {
return static_cast<GLProgram*>(_programs->objectForKey(key)); auto it = _programs.find(key);
if( it != _programs.end() )
return it->second;
return nullptr;
} }
void ShaderCache::addProgram(GLProgram* program, const char* key) void ShaderCache::addProgram(GLProgram* program, const std::string &key)
{ {
_programs->setObject(program, key); program->retain();
_programs.insert( std::make_pair( key, program) );
} }
NS_CC_END NS_CC_END

View File

@ -27,6 +27,9 @@ THE SOFTWARE.
#ifndef __CCSHADERCACHE_H__ #ifndef __CCSHADERCACHE_H__
#define __CCSHADERCACHE_H__ #define __CCSHADERCACHE_H__
#include <string>
#include <unordered_map>
#include "cocoa/CCDictionary.h" #include "cocoa/CCDictionary.h"
NS_CC_BEGIN NS_CC_BEGIN
@ -68,17 +71,17 @@ public:
void reloadDefaultShaders(); void reloadDefaultShaders();
/** returns a GL program for a given key */ /** returns a GL program for a given key */
GLProgram * programForKey(const char* key); GLProgram * programForKey(const std::string &key);
/** adds a GLProgram to the cache for a given name */ /** adds a GLProgram to the cache for a given name */
void addProgram(GLProgram* program, const char* key); void addProgram(GLProgram* program, const std::string &key);
private: private:
bool init(); bool init();
void loadDefaultShader(GLProgram *program, int type); void loadDefaultShader(GLProgram *program, int type);
Dictionary* _programs; // Dictionary* _programs;
std::unordered_map<std::string, GLProgram*> _programs;
}; };
// end of shaders group // end of shaders group

View File

@ -74,17 +74,14 @@ TextureCache::TextureCache()
, _asyncRefCount(0) , _asyncRefCount(0)
{ {
CCASSERT(_sharedTextureCache == nullptr, "Attempted to allocate a second instance of a singleton."); CCASSERT(_sharedTextureCache == nullptr, "Attempted to allocate a second instance of a singleton.");
_textures = new Dictionary();
_textures->init();
} }
TextureCache::~TextureCache() TextureCache::~TextureCache()
{ {
CCLOGINFO("deallocing TextureCache: %p", this); CCLOGINFO("deallocing TextureCache: %p", this);
CC_SAFE_RELEASE(_textures); for( auto it=_textures.begin(); it!=_textures.end(); ++it)
(it->second)->release();
CC_SAFE_DELETE(_loadingThread); CC_SAFE_DELETE(_loadingThread);
_sharedTextureCache = nullptr; _sharedTextureCache = nullptr;
@ -102,42 +99,34 @@ void TextureCache::destroyInstance()
const char* TextureCache::description() const const char* TextureCache::description() const
{ {
return String::createWithFormat("<TextureCache | Number of textures = %u>", _textures->count())->getCString(); return String::createWithFormat("<TextureCache | Number of textures = %lu>", _textures.size() )->getCString();
} }
Dictionary* TextureCache::snapshotTextures() //Dictionary* TextureCache::snapshotTextures()
{ //{
Dictionary* pRet = new Dictionary(); // Dictionary* pRet = new Dictionary();
DictElement* pElement = NULL; // DictElement* pElement = NULL;
CCDICT_FOREACH(_textures, pElement) // CCDICT_FOREACH(_textures, pElement)
{ // {
pRet->setObject(pElement->getObject(), pElement->getStrKey()); // pRet->setObject(pElement->getObject(), pElement->getStrKey());
} // }
pRet->autorelease(); // pRet->autorelease();
return pRet; // return pRet;
} //}
void TextureCache::addImageAsync(const char *path, Object *target, SEL_CallFuncO selector) void TextureCache::addImageAsync(const std::string &path, Object *target, SEL_CallFuncO selector)
{ {
CCASSERT(path != NULL, "TextureCache: fileimage MUST not be NULL");
Texture2D *texture = NULL; Texture2D *texture = NULL;
// optimization std::string fullpath = FileUtils::getInstance()->fullPathForFilename(path.c_str());
std::string pathKey = path; auto it = _textures.find(fullpath);
if( it != _textures.end() )
texture = it->second;
pathKey = FileUtils::getInstance()->fullPathForFilename(pathKey.c_str()); if (texture != NULL && target && selector)
texture = static_cast<Texture2D*>(_textures->objectForKey(pathKey.c_str()));
std::string fullpath = pathKey;
if (texture != NULL)
{ {
if (target && selector) (target->*selector)(texture);
{
(target->*selector)(texture);
}
return; return;
} }
@ -270,8 +259,10 @@ void TextureCache::addImageAsyncCallBack(float dt)
// cache the texture file name // cache the texture file name
VolatileTexture::addImageTexture(texture, filename); VolatileTexture::addImageTexture(texture, filename);
#endif #endif
// cache the texture // cache the texture. retain it, since it is added in the map
_textures->setObject(texture, filename); _textures.insert( std::make_pair(filename, texture) );
texture->retain();
texture->autorelease(); texture->autorelease();
if (target && selector) if (target && selector)
@ -292,27 +283,25 @@ void TextureCache::addImageAsyncCallBack(float dt)
} }
} }
Texture2D * TextureCache::addImage(const char * path) Texture2D * TextureCache::addImage(const std::string &path)
{ {
CCASSERT(path != NULL, "TextureCache: fileimage MUST not be NULL");
Texture2D * texture = NULL; Texture2D * texture = NULL;
Image* image = NULL; Image* image = NULL;
// Split up directory and filename // Split up directory and filename
// MUTEX: // MUTEX:
// Needed since addImageAsync calls this method from a different thread // Needed since addImageAsync calls this method from a different thread
std::string pathKey = FileUtils::getInstance()->fullPathForFilename(path); std::string fullpath = FileUtils::getInstance()->fullPathForFilename(path.c_str());
if (pathKey.size() == 0) if (fullpath.size() == 0)
{ {
return NULL; return NULL;
} }
texture = static_cast<Texture2D*>(_textures->objectForKey(pathKey)); auto it = _textures.find(fullpath);
if( it != _textures.end() )
texture = it->second;
if (! texture) if (! texture)
{ {
std::string fullpath(pathKey);
// all images are handled by UIImage except PVR extension that is handled by our own handler // all images are handled by UIImage except PVR extension that is handled by our own handler
do do
{ {
@ -330,12 +319,12 @@ Texture2D * TextureCache::addImage(const char * path)
// cache the texture file name // cache the texture file name
VolatileTexture::addImageTexture(texture, fullpath.c_str()); VolatileTexture::addImageTexture(texture, fullpath.c_str());
#endif #endif
_textures->setObject(texture, pathKey); // texture already retained, no need to re-retain it
texture->release(); _textures.insert( std::make_pair(fullpath, texture) );
} }
else else
{ {
CCLOG("cocos2d: Couldn't create texture for file:%s in TextureCache", path); CCLOG("cocos2d: Couldn't create texture for file:%s in TextureCache", path.c_str());
} }
} while (0); } while (0);
} }
@ -345,7 +334,7 @@ Texture2D * TextureCache::addImage(const char * path)
return texture; return texture;
} }
Texture2D* TextureCache::addImage(Image *image, const char *key) Texture2D* TextureCache::addImage(Image *image, const std::string &key)
{ {
CCASSERT(image != NULL, "TextureCache: image MUST not be nil"); CCASSERT(image != NULL, "TextureCache: image MUST not be nil");
@ -353,9 +342,9 @@ Texture2D* TextureCache::addImage(Image *image, const char *key)
do do
{ {
// If key is nil, then create a new texture each time auto it = _textures.find(key);
if(key && (texture = static_cast<Texture2D*>(_textures->objectForKey(key))) ) if( it != _textures.end() ) {
{ texture = it->second;
break; break;
} }
@ -363,9 +352,11 @@ Texture2D* TextureCache::addImage(Image *image, const char *key)
texture = new Texture2D(); texture = new Texture2D();
texture->initWithImage(image); texture->initWithImage(image);
if(key && texture) if(texture)
{ {
_textures->setObject(texture, key); _textures.insert( std::make_pair(key, texture) );
texture->retain();
texture->autorelease(); texture->autorelease();
} }
else else
@ -386,48 +377,25 @@ Texture2D* TextureCache::addImage(Image *image, const char *key)
void TextureCache::removeAllTextures() void TextureCache::removeAllTextures()
{ {
_textures->removeAllObjects(); for( auto it=_textures.begin(); it!=_textures.end(); ++it ) {
(it->second)->release();
}
_textures.clear();
} }
void TextureCache::removeUnusedTextures() void TextureCache::removeUnusedTextures()
{ {
/* for( auto it=_textures.cbegin(); it!=_textures.cend(); /* nothing */) {
DictElement* pElement = NULL; Texture2D *tex = it->second;
CCDICT_FOREACH(_textures, pElement) if( tex->retainCount() == 1 ) {
{ CCLOG("cocos2d: TextureCache: removing unused texture: %s", it->first.c_str());
CCLOG("cocos2d: TextureCache: texture: %s", pElement->getStrKey());
Texture2D *value = static_cast<Texture2D*>(pElement->getObject());
if (value->retainCount() == 1)
{
CCLOG("cocos2d: TextureCache: removing unused texture: %s", pElement->getStrKey());
_textures->removeObjectForElememt(pElement);
}
}
*/
/** Inter engineer zhuoshi sun finds that this way will get better performance tex->release();
*/ _textures.erase(it++);
if (_textures->count()) } else {
{ ++it;
// find elements to be removed
DictElement* pElement = NULL;
list<DictElement*> elementToRemove;
CCDICT_FOREACH(_textures, pElement)
{
CCLOG("cocos2d: TextureCache: texture: %s", pElement->getStrKey());
Texture2D *value = static_cast<Texture2D*>(pElement->getObject());
if (value->retainCount() == 1)
{
elementToRemove.push_back(pElement);
}
} }
// remove elements
for (auto iter = elementToRemove.begin(); iter != elementToRemove.end(); ++iter)
{
CCLOG("cocos2d: TextureCache: removing unused texture: %s", (*iter)->getStrKey());
_textures->removeObjectForElememt(*iter);
}
} }
} }
@ -438,24 +406,31 @@ void TextureCache::removeTexture(Texture2D* texture)
return; return;
} }
Array* keys = _textures->allKeysForObject(texture); for( auto it=_textures.cbegin(); it!=_textures.cend(); /* nothing */ ) {
_textures->removeObjectsForKeys(keys); if( it->second == texture ) {
} texture->release();
_textures.erase(it++);
void TextureCache::removeTextureForKey(const char *textureKeyName) break;
{ } else
if (textureKeyName == NULL) ++it;
{
return;
} }
string fullPath = FileUtils::getInstance()->fullPathForFilename(textureKeyName);
_textures->removeObjectForKey(fullPath);
} }
Texture2D* TextureCache::textureForKey(const char* key) void TextureCache::removeTextureForKey(const std::string &textureKeyName)
{ {
return static_cast<Texture2D*>(_textures->objectForKey(FileUtils::getInstance()->fullPathForFilename(key))); auto it = _textures.find(textureKeyName);
if( it != _textures.end() ) {
(it->second)->release();
_textures.erase(it);
}
}
Texture2D* TextureCache::getTextureForKey(const std::string &key) const
{
auto it = _textures.find(key);
if( it != _textures.end() )
return it->second;
return NULL;
} }
void TextureCache::reloadAllTextures() void TextureCache::reloadAllTextures()
@ -465,22 +440,21 @@ void TextureCache::reloadAllTextures()
#endif #endif
} }
void TextureCache::dumpCachedTextureInfo() void TextureCache::dumpCachedTextureInfo() const
{ {
unsigned int count = 0; unsigned int count = 0;
unsigned int totalBytes = 0; unsigned int totalBytes = 0;
DictElement* pElement = NULL; for( auto it = _textures.begin(); it != _textures.end(); ++it ) {
CCDICT_FOREACH(_textures, pElement)
{ Texture2D* tex = it->second;
Texture2D* tex = static_cast<Texture2D*>(pElement->getObject());
unsigned int bpp = tex->getBitsPerPixelForFormat(); unsigned int bpp = tex->getBitsPerPixelForFormat();
// Each texture takes up width * height * bytesPerPixel bytes. // Each texture takes up width * height * bytesPerPixel bytes.
unsigned int bytes = tex->getPixelsWide() * tex->getPixelsHigh() * bpp / 8; unsigned int bytes = tex->getPixelsWide() * tex->getPixelsHigh() * bpp / 8;
totalBytes += bytes; totalBytes += bytes;
count++; count++;
CCLOG("cocos2d: \"%s\" rc=%lu id=%lu %lu x %lu @ %ld bpp => %lu KB", log("cocos2d: \"%s\" rc=%lu id=%lu %lu x %lu @ %ld bpp => %lu KB",
pElement->getStrKey(), it->first.c_str(),
(long)tex->retainCount(), (long)tex->retainCount(),
(long)tex->getName(), (long)tex->getName(),
(long)tex->getPixelsWide(), (long)tex->getPixelsWide(),
@ -489,7 +463,7 @@ void TextureCache::dumpCachedTextureInfo()
(long)bytes / 1024); (long)bytes / 1024);
} }
CCLOG("cocos2d: TextureCache dumpDebugInfo: %ld textures, for %lu KB (%.2f MB)", (long)count, (long)totalBytes / 1024, totalBytes / (1024.0f*1024.0f)); log("cocos2d: TextureCache dumpDebugInfo: %ld textures, for %lu KB (%.2f MB)", (long)count, (long)totalBytes / 1024, totalBytes / (1024.0f*1024.0f));
} }
#if CC_ENABLE_CACHE_TEXTURE_DATA #if CC_ENABLE_CACHE_TEXTURE_DATA

View File

@ -33,9 +33,9 @@ THE SOFTWARE.
#include <condition_variable> #include <condition_variable>
#include <queue> #include <queue>
#include <string> #include <string>
#include <unordered_map>
#include "cocoa/CCObject.h" #include "cocoa/CCObject.h"
#include "cocoa/CCDictionary.h"
#include "textures/CCTexture2D.h" #include "textures/CCTexture2D.h"
#include "platform/CCImage.h" #include "platform/CCImage.h"
@ -83,7 +83,7 @@ public:
const char* description(void) const; const char* description(void) const;
Dictionary* snapshotTextures(); // Dictionary* snapshotTextures();
/** Returns a Texture2D object given an filename. /** Returns a Texture2D object given an filename.
* If the filename was not previously loaded, it will create a new Texture2D * If the filename was not previously loaded, it will create a new Texture2D
@ -91,7 +91,7 @@ public:
* Otherwise it will return a reference of a previously loaded image. * Otherwise it will return a reference of a previously loaded image.
* Supported image extensions: .png, .bmp, .tiff, .jpeg, .pvr, .gif * Supported image extensions: .png, .bmp, .tiff, .jpeg, .pvr, .gif
*/ */
Texture2D* addImage(const char* fileimage); Texture2D* addImage(const std::string &filepath);
/* Returns a Texture2D object given a file image /* Returns a Texture2D object given a file image
* If the file image was not previously loaded, it will create a new Texture2D object and it will return it. * If the file image was not previously loaded, it will create a new Texture2D object and it will return it.
@ -100,7 +100,7 @@ public:
* Supported image extensions: .png, .jpg * Supported image extensions: .png, .jpg
* @since v0.8 * @since v0.8
*/ */
virtual void addImageAsync(const char *path, Object *target, SEL_CallFuncO selector); virtual void addImageAsync(const std::string &filepath, Object *target, SEL_CallFuncO selector);
/** Returns a Texture2D object given an Image. /** Returns a Texture2D object given an Image.
* If the image was not previously loaded, it will create a new Texture2D object and it will return it. * If the image was not previously loaded, it will create a new Texture2D object and it will return it.
@ -108,13 +108,14 @@ public:
* The "key" parameter will be used as the "key" for the cache. * The "key" parameter will be used as the "key" for the cache.
* If "key" is nil, then a new texture will be created each time. * If "key" is nil, then a new texture will be created each time.
*/ */
Texture2D* addImage(Image *image, const char *key); Texture2D* addImage(Image *image, const std::string &key);
CC_DEPRECATED_ATTRIBUTE Texture2D* addUIImage(Image *image, const char *key) { return addImage(image,key); } CC_DEPRECATED_ATTRIBUTE Texture2D* addUIImage(Image *image, const char *key) { return addImage(image,key); }
/** Returns an already created texture. Returns nil if the texture doesn't exist. /** Returns an already created texture. Returns nil if the texture doesn't exist.
@since v0.99.5 @since v0.99.5
*/ */
Texture2D* textureForKey(const char* key); Texture2D* getTextureForKey(const std::string& key) const;
CC_DEPRECATED_ATTRIBUTE Texture2D* textureForKey(const char* key) const { return getTextureForKey(key); }
/** Purges the dictionary of loaded textures. /** Purges the dictionary of loaded textures.
* Call this method if you receive the "Memory Warning" * Call this method if you receive the "Memory Warning"
@ -138,14 +139,14 @@ public:
/** Deletes a texture from the cache given a its key name /** Deletes a texture from the cache given a its key name
@since v0.99.4 @since v0.99.4
*/ */
void removeTextureForKey(const char *textureKeyName); void removeTextureForKey(const std::string &key);
/** Output to CCLOG the current contents of this TextureCache /** Output to CCLOG the current contents of this TextureCache
* This will attempt to calculate the size of each texture, and the total texture memory in use * This will attempt to calculate the size of each texture, and the total texture memory in use
* *
* @since v1.0 * @since v1.0
*/ */
void dumpCachedTextureInfo(); void dumpCachedTextureInfo() const;
private: private:
void addImageAsyncCallBack(float dt); void addImageAsyncCallBack(float dt);
@ -157,9 +158,9 @@ public:
public: public:
AsyncStruct(const std::string& fn, Object *t, SEL_CallFuncO s) : filename(fn), target(t), selector(s) {} AsyncStruct(const std::string& fn, Object *t, SEL_CallFuncO s) : filename(fn), target(t), selector(s) {}
std::string filename; std::string filename;
Object *target; Object *target;
SEL_CallFuncO selector; SEL_CallFuncO selector;
}; };
protected: protected:
@ -184,7 +185,7 @@ protected:
int _asyncRefCount; int _asyncRefCount;
Dictionary* _textures; std::unordered_map<std::string, Texture2D*> _textures;
static TextureCache *_sharedTextureCache; static TextureCache *_sharedTextureCache;
}; };