分析cocos緩存管理的實現

今天看了一下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,0,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, sizeof(buftmp)-1, "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://blog.csdn.net/langresser_king/article/details/8659538

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://blog.csdn.net/langresser_king/article/details/8659538

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章