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

View File

@ -27,6 +27,9 @@ THE SOFTWARE.
#ifndef __CCSHADERCACHE_H__
#define __CCSHADERCACHE_H__
#include <string>
#include <unordered_map>
#include "cocoa/CCDictionary.h"
NS_CC_BEGIN
@ -68,17 +71,17 @@ public:
void reloadDefaultShaders();
/** 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 */
void addProgram(GLProgram* program, const char* key);
void addProgram(GLProgram* program, const std::string &key);
private:
bool init();
void loadDefaultShader(GLProgram *program, int type);
Dictionary* _programs;
// Dictionary* _programs;
std::unordered_map<std::string, GLProgram*> _programs;
};
// end of shaders group

View File

@ -74,17 +74,14 @@ TextureCache::TextureCache()
, _asyncRefCount(0)
{
CCASSERT(_sharedTextureCache == nullptr, "Attempted to allocate a second instance of a singleton.");
_textures = new Dictionary();
_textures->init();
}
TextureCache::~TextureCache()
{
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);
_sharedTextureCache = nullptr;
@ -102,42 +99,34 @@ void TextureCache::destroyInstance()
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* pRet = new Dictionary();
DictElement* pElement = NULL;
CCDICT_FOREACH(_textures, pElement)
{
pRet->setObject(pElement->getObject(), pElement->getStrKey());
}
pRet->autorelease();
return pRet;
}
//Dictionary* TextureCache::snapshotTextures()
//{
// Dictionary* pRet = new Dictionary();
// DictElement* pElement = NULL;
// CCDICT_FOREACH(_textures, pElement)
// {
// pRet->setObject(pElement->getObject(), pElement->getStrKey());
// }
// pRet->autorelease();
// 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;
// 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());
texture = static_cast<Texture2D*>(_textures->objectForKey(pathKey.c_str()));
std::string fullpath = pathKey;
if (texture != NULL)
if (texture != NULL && target && selector)
{
if (target && selector)
{
(target->*selector)(texture);
}
(target->*selector)(texture);
return;
}
@ -270,8 +259,10 @@ void TextureCache::addImageAsyncCallBack(float dt)
// cache the texture file name
VolatileTexture::addImageTexture(texture, filename);
#endif
// cache the texture
_textures->setObject(texture, filename);
// cache the texture. retain it, since it is added in the map
_textures.insert( std::make_pair(filename, texture) );
texture->retain();
texture->autorelease();
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;
Image* image = NULL;
// Split up directory and filename
// MUTEX:
// Needed since addImageAsync calls this method from a different thread
std::string pathKey = FileUtils::getInstance()->fullPathForFilename(path);
if (pathKey.size() == 0)
std::string fullpath = FileUtils::getInstance()->fullPathForFilename(path.c_str());
if (fullpath.size() == 0)
{
return NULL;
}
texture = static_cast<Texture2D*>(_textures->objectForKey(pathKey));
auto it = _textures.find(fullpath);
if( it != _textures.end() )
texture = it->second;
if (! texture)
{
std::string fullpath(pathKey);
// all images are handled by UIImage except PVR extension that is handled by our own handler
do
{
@ -330,12 +319,12 @@ Texture2D * TextureCache::addImage(const char * path)
// cache the texture file name
VolatileTexture::addImageTexture(texture, fullpath.c_str());
#endif
_textures->setObject(texture, pathKey);
texture->release();
// texture already retained, no need to re-retain it
_textures.insert( std::make_pair(fullpath, texture) );
}
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);
}
@ -345,7 +334,7 @@ Texture2D * TextureCache::addImage(const char * path)
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");
@ -353,9 +342,9 @@ Texture2D* TextureCache::addImage(Image *image, const char *key)
do
{
// If key is nil, then create a new texture each time
if(key && (texture = static_cast<Texture2D*>(_textures->objectForKey(key))) )
{
auto it = _textures.find(key);
if( it != _textures.end() ) {
texture = it->second;
break;
}
@ -363,9 +352,11 @@ Texture2D* TextureCache::addImage(Image *image, const char *key)
texture = new Texture2D();
texture->initWithImage(image);
if(key && texture)
if(texture)
{
_textures->setObject(texture, key);
_textures.insert( std::make_pair(key, texture) );
texture->retain();
texture->autorelease();
}
else
@ -386,48 +377,25 @@ Texture2D* TextureCache::addImage(Image *image, const char *key)
void TextureCache::removeAllTextures()
{
_textures->removeAllObjects();
for( auto it=_textures.begin(); it!=_textures.end(); ++it ) {
(it->second)->release();
}
_textures.clear();
}
void TextureCache::removeUnusedTextures()
{
/*
DictElement* pElement = NULL;
CCDICT_FOREACH(_textures, pElement)
{
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
*/
if (_textures->count())
{
// 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);
for( auto it=_textures.cbegin(); it!=_textures.cend(); /* nothing */) {
Texture2D *tex = it->second;
if( tex->retainCount() == 1 ) {
CCLOG("cocos2d: TextureCache: removing unused texture: %s", it->first.c_str());
tex->release();
_textures.erase(it++);
} else {
++it;
}
}
}
@ -438,24 +406,31 @@ void TextureCache::removeTexture(Texture2D* texture)
return;
}
Array* keys = _textures->allKeysForObject(texture);
_textures->removeObjectsForKeys(keys);
}
void TextureCache::removeTextureForKey(const char *textureKeyName)
{
if (textureKeyName == NULL)
{
return;
for( auto it=_textures.cbegin(); it!=_textures.cend(); /* nothing */ ) {
if( it->second == texture ) {
texture->release();
_textures.erase(it++);
break;
} else
++it;
}
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()
@ -465,22 +440,21 @@ void TextureCache::reloadAllTextures()
#endif
}
void TextureCache::dumpCachedTextureInfo()
void TextureCache::dumpCachedTextureInfo() const
{
unsigned int count = 0;
unsigned int totalBytes = 0;
DictElement* pElement = NULL;
CCDICT_FOREACH(_textures, pElement)
{
Texture2D* tex = static_cast<Texture2D*>(pElement->getObject());
for( auto it = _textures.begin(); it != _textures.end(); ++it ) {
Texture2D* tex = it->second;
unsigned int bpp = tex->getBitsPerPixelForFormat();
// Each texture takes up width * height * bytesPerPixel bytes.
unsigned int bytes = tex->getPixelsWide() * tex->getPixelsHigh() * bpp / 8;
totalBytes += bytes;
count++;
CCLOG("cocos2d: \"%s\" rc=%lu id=%lu %lu x %lu @ %ld bpp => %lu KB",
pElement->getStrKey(),
log("cocos2d: \"%s\" rc=%lu id=%lu %lu x %lu @ %ld bpp => %lu KB",
it->first.c_str(),
(long)tex->retainCount(),
(long)tex->getName(),
(long)tex->getPixelsWide(),
@ -489,7 +463,7 @@ void TextureCache::dumpCachedTextureInfo()
(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

View File

@ -33,9 +33,9 @@ THE SOFTWARE.
#include <condition_variable>
#include <queue>
#include <string>
#include <unordered_map>
#include "cocoa/CCObject.h"
#include "cocoa/CCDictionary.h"
#include "textures/CCTexture2D.h"
#include "platform/CCImage.h"
@ -83,7 +83,7 @@ public:
const char* description(void) const;
Dictionary* snapshotTextures();
// Dictionary* snapshotTextures();
/** Returns a Texture2D object given an filename.
* 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.
* 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
* 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
* @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.
* 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.
* 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); }
/** Returns an already created texture. Returns nil if the texture doesn't exist.
@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.
* 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
@since v0.99.4
*/
void removeTextureForKey(const char *textureKeyName);
void removeTextureForKey(const std::string &key);
/** 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
*
* @since v1.0
*/
void dumpCachedTextureInfo();
void dumpCachedTextureInfo() const;
private:
void addImageAsyncCallBack(float dt);
@ -157,9 +158,9 @@ public:
public:
AsyncStruct(const std::string& fn, Object *t, SEL_CallFuncO s) : filename(fn), target(t), selector(s) {}
std::string filename;
Object *target;
SEL_CallFuncO selector;
std::string filename;
Object *target;
SEL_CallFuncO selector;
};
protected:
@ -184,7 +185,7 @@ protected:
int _asyncRefCount;
Dictionary* _textures;
std::unordered_map<std::string, Texture2D*> _textures;
static TextureCache *_sharedTextureCache;
};