-
今天看了一下cocos的缓存管理类,首先缓存管理类是一个单例对象,获得该对象的实例必须通过导演类Dirctor的Director::getInstance()->getTextureCache();其实这是一种挺不错的架构方式,通过一个在顶部的对象来获得引擎里面你需要的接口,这就屏蔽了实现的细节。题外话了,转入正题。cocos的纹理缓存管理包含了加载图片纹理的接口,删除纹理对象的接口,删除无用纹理对象的接口(纹理对象的引用计数值为1),获得纹理缓存大小的接口,重新加载纹理对象的接口。在加载纹理对象的接口当中包含了异步加载和普通加载。异步加载是使用了线程机制把图片放入缓存里面,然后在加载完成以后会调用用户传进来的函数,这个机制可以实现加载进度条的实现。在异步加载中loadimage是一直阻塞的(使用了条件变量),只有当需要加载图片或者是调用了导演类的purgeDirector的时候才会被唤醒。普通加载就是直接把纹理对象放入缓存里面。异步加载与普通加载的接口分别是addImageAsync,addImage,其中异步加载可以传用户自定义函数,但加载完成会回调该函数。其余的在下面的代码将详细的注释讲解: ”’c++ NS_CC_BEGIN //单例模式实现纹理缓存 TextureCache * TextureCache::getInstance() { return Director::getInstance()->getTextureCache(); } TextureCache::TextureCache()
-
_loadingThread(nullptr)
,_asyncStructQueue(nullptr)
,_imageInfoQueue(nullptr)
,_needQuit(false)
,_asyncRefCount(0)
{
}
TextureCache::~TextureCache()
{
CCLOGINFO(“deallocing TextureCache: %p”,this);
//在析构函数里面释放map里面保存的指针,
//防止内存泄露
for( auto it=_textures.begin(); it!=_textures.end(); ++it)
(it->second)->release();
CC_SAFE_DELETE(_loadingThread);
}
void TextureCache::destroyInstance()
{
}
//获得纹理缓存的对象其实还是从导演类获得
TextureCache * TextureCache::sharedTextureCache()
{
return Director::getInstance()->getTextureCache();
}
//已抛弃,使用destroyInstance
void TextureCache::purgeSharedTextureCache()
{
}
//获得缓存的大小
std::string TextureCache::getDescription() const
{
return StringUtils::format(“
if CC_ENABLE_CACHE_TEXTURE_DATA
// cache the texture file name VolatileTextureMgr::addImageTexture(texture,filename);
endif
//加入到map里面存储起来
// cache the texture. retain it,since it is added in the map
_textures.insert( std::make_pair(filename,texture) );
texture->retain();
texture->autorelease();
}
else
{
//已经在缓存里面,从map里面取出
auto it = _textures.find(asyncStruct->filename);
if(it != _textures.end())
texture = it->second;
}
//表示图片已经加载成功,回调用户传进来的用户函数
if (asyncStruct->callback)
{
asyncStruct->callback(texture);
}
//如果image不为空则释放image对象
if(image)
{
image->release();
}
delete asyncStruct;
delete imageInfo;
//已经加载的图片后则把引用计数减1
–_asyncRefCount;
//当所有图片已经完成加载则停止执行计数器函数
if (0 == _asyncRefCount)
{
Director::getInstance()->getScheduler()->unschedule(schedule_selector(TextureCache::addImageAsyncCallBack),this);
}
}
}
//普通加载函数重路径加载
//判断图片是否在缓存里面,如果不在则重新
//生成纹理对象,并把纹理对象放入缓存里面,否则直接重缓存里面取出
Texture2D * TextureCache::addImage(const std::string &path)
{
Texture2D * texture = nullptr;
Image* image = nullptr;
// Split up directory and filename
// MUTEX:
// Needed since addImageAsync calls this method from a different thread
std::string fullpath = FileUtils::getInstance()->fullPathForFilename(path);
if (fullpath.size() == 0)
{
return nullptr;
}
auto it = _textures.find(fullpath);
if( it != _textures.end() )
texture = it->second;
if (! texture)
{
// all images are handled by UIImage except PVR extension that is handled by our own handler
do
{
image = new Image();
CC_BREAK_IF(nullptr == image);
bool bRet = image->initWithImageFile(fullpath);
CC_BREAK_IF(!bRet);
texture = new Texture2D();
if( texture && texture->initWithImage(image) )
{
if CC_ENABLE_CACHE_TEXTURE_DATA
// cache the texture file name VolatileTextureMgr::addImageTexture(texture,fullpath);
endif
// 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.c_str()); } } while (0); } CC_SAFE_RELEASE(image); return texture;
}
//根据传进的Key值判断是否在缓存里面,在的话直接返回纹理对象
//否则重新生成纹理对象并放入缓存里面
Texture2D* TextureCache::addImage(Image *image,const std::string &key)
{
CCASSERT(image != nullptr,“TextureCache: image MUST not be nil”);
Texture2D * texture = nullptr;
do
{
auto it = _textures.find(key);
if( it != _textures.end() ) {
texture = it->second;
break;
}
// prevents overloading the autorelease pool
texture = new Texture2D();
texture->initWithImage(image);
if(texture)
{
_textures.insert( std::make_pair(key,texture) );
texture->retain();
texture->autorelease();
}
else
{
CCLOG(“cocos2d: Couldn’t add UIImage in TextureCache”);
}
} while (0);
if CC_ENABLE_CACHE_TEXTURE_DATA
VolatileTextureMgr::addImage(texture,image);
endif
return texture;
}
//重新加载纹理成功返回true,否则false
bool TextureCache::reloadTexture(const std::string& fileName)
{
Texture2D * texture = nullptr;
std::string fullpath = FileUtils::getInstance()->fullPathForFilename(fileName);
if (fullpath.size() == 0)
{
return false;
}
auto it = _textures.find(fullpath);
if (it != _textures.end()) {
texture = it->second;
}
//当传进来的路径的纹理不在缓存里面则调用addImage
//放入缓存里面,否则重新生成纹理对象的图片信息
bool ret = false;
if (! texture) {
texture = this->addImage(fullpath);
ret = (texture != nullptr);
}
else
{
do {
Image* image = new Image();
CC_BREAK_IF(nullptr == image);
bool bRet = image->initWithImageFile(fullpath);
CC_BREAK_IF(!bRet);
ret = texture->initWithImage(image); } while (0); } return ret;
}
// TextureCache - Remove
//重缓存里面删除所有的纹理对象
void TextureCache::removeAllTextures()
{
for( auto it=_textures.begin(); it!=_textures.end(); ++it ) {
(it->second)->release();
}
_textures.clear();
}
//根据纹理对象的引用计数值,当为1时删除该纹理对象
void TextureCache::removeUnusedTextures()
{
for( auto it=_textures.cbegin(); it!=_textures.cend(); /* nothing */) {
Texture2D *tex = it->second;
if( tex->getReferenceCount() == 1 ) {
CCLOG(“cocos2d: TextureCache: removing unused texture: %s”,it->first.c_str());
tex->release();
_textures.erase(it++);
} else {
++it;
}
}
}
//重存储缓存的map里面取出对于的纹理对象并释放该对象
void TextureCache::removeTexture(Texture2D* texture)
{
if( ! texture )
{
return;
}
for( auto it=_textures.cbegin(); it!=_textures.cend(); /* nothing */ ) {
if( it->second == texture ) {
texture->release();
_textures.erase(it++);
break;
} else
++it;
}
}
//根据Key值删除纹理对象
void TextureCache::removeTextureForKey(const std::string &textureKeyName)
{
std::string key = textureKeyName;
auto it = _textures.find(key);
//如果没有在缓存里面找到则重新获得该纹理对象的真实路径值在寻找一遍
if( it == _textures.end() ) {
key = FileUtils::getInstance()->fullPathForFilename(textureKeyName);
it = _textures.find(key);
}
//如果寻找到该对象则删除,否则不做处理
if( it != _textures.end() ) {
(it->second)->release();
_textures.erase(it);
}
}
//根据Key获得纹理对象
Texture2D* TextureCache::getTextureForKey(const std::string &textureKeyName) const
{
std::string key = textureKeyName;
auto it = _textures.find(key);
//如果没有找到纹理对象,则取出纹理对象的路径,在寻找一遍
if( it == _textures.end() ) {
key = FileUtils::getInstance()->fullPathForFilename(textureKeyName);
it = _textures.find(key);
}
//找到后则返回
if( it != _textures.end() )
return it->second;
return nullptr;
}
//不做任何事情
void TextureCache::reloadAllTextures()
{
//will do nothing
// #if CC_ENABLE_CACHE_TEXTURE_DATA
// VolatileTextureMgr::reloadAllTextures();
// #endif
}
//由diretor调用线程loadiamg线程将会跑完
void TextureCache::waitForQuit()
{
// notify sub thread to quick
_needQuit = true;
_sleepCondition.notify_one();
if (_loadingThread) _loadingThread->join();
}
//获得所有缓存的大小
std::string TextureCache::getCachedTextureInfo() const
{
std::string buffer;
char buftmp[4096];
unsigned int count = 0;
unsigned int totalBytes = 0;
// 遍历map获得所有的缓存内存信息
for( auto it = _textures.begin(); it != _textures.end(); ++it ) {
memset(buftmp,sizeof(buftmp));
Texture2D* tex = it->second;
unsigned int bpp = tex->getBitsPerPixelForFormat();
// Each texture takes up width * height * bytesPerPixel bytes.
auto bytes = tex->getPixelsWide() * tex->getPixelsHigh() * bpp / 8;
totalBytes += bytes;
count++;
snprintf(buftmp,sizeof(buftmp)-1,”\”%s\” rc=%lu id=%lu %lu x %lu @ %ld bpp => %lu KB\n”,
it->first.c_str(),
(long)tex->getReferenceCount(),
(long)tex->getName(),
(long)tex->getPixelsWide(),
(long)tex->getPixelsHigh(),
(long)bpp,
(long)bytes / 1024);
buffer += buftmp; } snprintf(buftmp,"TextureCache dumpDebugInfo: %ld textures,for %lu KB (%.2f MB)\n",(long)count,(long)totalBytes / 1024,totalBytes / (1024.0f*1024.0f)); buffer += buftmp; return buffer;
}
//这段代码是查阅了相关的资料得知android wp8在程序切入后台时会使得纹理失效,所以cocos在游戏切入后台时会把纹理缓存起来,在程序恢复时在重新加载内存。好像这样会出现游戏从后台恢复到前台会黑屏的现象,是因为后台纹理太多了,恢复纹理需要一定的时间,所以感觉就黑屏卡主的感觉。解决方案:参考http://www.jb51.cc/article/p-yldmzkog-kx.html
if CC_ENABLE_CACHE_TEXTURE_DATA
- std::list VolatileTextureMgr::_textures;
- bool VolatileTextureMgr::_isReloading = false;
- VolatileTexture::VolatileTexture(Texture2D *t)
-
_texture(t)
,_cashedImageType(kInvalid)
,_textureData(nullptr)
,_pixelFormat(Texture2D::PixelFormat::RGBA8888)
,_fileName(“”)
,_text(“”)
,_uiImage(nullptr)
,_hasMipmaps(false)
{
_texParams.minFilter = GL_LINEAR;
_texParams.magFilter = GL_LINEAR;
_texParams.wrapS = GL_CLAMP_TO_EDGE;
_texParams.wrapT = GL_CLAMP_TO_EDGE;
}
VolatileTexture::~VolatileTexture()
{
CC_SAFE_RELEASE(_uiImage);
}
//从图片中加载
void VolatileTextureMgr::addImageTexture(Texture2D *tt,const std::string& imageFileName)
{
if (_isReloading)
{
return;
}
VolatileTexture *vt = findVolotileTexture(tt);
vt->_cashedImageType = VolatileTexture::kImageFile;
vt->_fileName = imageFileName;
vt->_pixelFormat = tt->getPixelFormat();
}
//从纹理对象中加载
void VolatileTextureMgr::addImage(Texture2D *tt,Image *image)
{
VolatileTexture *vt = findVolotileTexture(tt);
image->retain();
vt->_uiImage = image;
vt->_cashedImageType = VolatileTexture::kImage;
}
//寻找该纹理对象是否在缓存里面如不在则重新生成VolatileTexture 对象并放入缓存里面
VolatileTexture* VolatileTextureMgr::findVolotileTexture(Texture2D *tt)
{
VolatileTexture *vt = 0;
auto i = _textures.begin();
while (i != _textures.end())
{
VolatileTexture *v = *i++;
if (v->_texture == tt)
{
vt = v;
break;
}
}if (! vt)
{
vt = new VolatileTexture(tt);
_textures.push_back(vt);
}return vt;
}
//从纹理数据加载
void VolatileTextureMgr::addDataTexture(Texture2D tt,void data,int dataLen,Texture2D::PixelFormat pixelFormat,const Size& contentSize)
{
if (_isReloading)
{
return;
}
VolatileTexture *vt = findVolotileTexture(tt);
vt->_cashedImageType = VolatileTexture::kImageData;
vt->_textureData = data;
vt->_dataLen = dataLen;
vt->_pixelFormat = pixelFormat;
vt->_textureSize = contentSize;
}
//加载字体纹理
void VolatileTextureMgr::addStringTexture(Texture2D tt,const char text,const FontDefinition& fontDefinition)
{
if (_isReloading)
{
return;
}
VolatileTexture *vt = findVolotileTexture(tt);
vt->_cashedImageType = VolatileTexture::kString;
vt->_text = text;
vt->_fontDefinition = fontDefinition;
}
//设置多重纹理
void VolatileTextureMgr::setHasMipmaps(Texture2D *t,bool hasMipmaps)
{
VolatileTexture *vt = findVolotileTexture(t);
vt->_hasMipmaps = hasMipmaps;
}
//设置纹理参数
void VolatileTextureMgr::setTexParameters(Texture2D *t,const Texture2D::TexParams &texParams)
{
VolatileTexture *vt = findVolotileTexture(t);
if (texParams.minFilter != GL_NONE)
vt->_texParams.minFilter = texParams.minFilter;
if (texParams.magFilter != GL_NONE)
vt->_texParams.magFilter = texParams.magFilter;
if (texParams.wrapS != GL_NONE)
vt->_texParams.wrapS = texParams.wrapS;
if (texParams.wrapT != GL_NONE)
vt->_texParams.wrapT = texParams.wrapT;
}
//从缓存表中删除该纹理对象
void VolatileTextureMgr::removeTexture(Texture2D *t)
{
auto i = _textures.begin();
while (i != _textures.end())
{
VolatileTexture *vt = *i++;
if (vt->_texture == t)
{
_textures.remove(vt);
delete vt;
break;
}
}
}
//重新载入所有的纹理对象
void VolatileTextureMgr::reloadAllTextures()
{
_isReloading = true;
CCLOG(“reload all texture”);
auto iter = _textures.begin();
while (iter != _textures.end())
{
VolatileTexture *vt = *iter++;
switch (vt->_cashedImageType)
{
case VolatileTexture::kImageFile:
{
Image* image = new Image();Data data = FileUtils::getInstance()->getDataFromFile(vt->_fileName); if (image && image->initWithImageData(data.getBytes(),data.getSize())) { Texture2D::PixelFormat oldPixelFormat = Texture2D::getDefaultAlphaPixelFormat(); Texture2D::setDefaultAlphaPixelFormat(vt->_pixelFormat); vt->_texture->initWithImage(image); Texture2D::setDefaultAlphaPixelFormat(oldPixelFormat); } CC_SAFE_RELEASE(image); } break; case VolatileTexture::kImageData: { vt->_texture->initWithData(vt->_textureData,vt->_dataLen,vt->_pixelFormat,vt->_textureSize.width,vt->_textureSize.height,vt->_textureSize); } break; case VolatileTexture::kString: { vt->_texture->initWithString(vt->_text.c_str(),vt->_fontDefinition); } break; case VolatileTexture::kImage: { vt->_texture->initWithImage(vt->_uiImage); } break; default: break; } if (vt->_hasMipmaps) { vt->_texture->generateMipmap(); } vt->_texture->setTexParameters(vt->_texParams);
}
_isReloading = false;
}
endif // CC_ENABLE_CACHE_TEXTURE_DATA
NS_CC_END
“`
参考:http://www.jb51.cc/article/p-yldmzkog-kx.html