天天看點

分析cocos緩存管理的實作if CC_ENABLE_CACHE_TEXTURE_DATAendifif CC_ENABLE_CACHE_TEXTURE_DATAendifif CC_ENABLE_CACHE_TEXTURE_DATAendifif CC_ENABLE_CACHE_TEXTURE_DATAendif // CC_ENABLE_CACHE_TEXTURE_DATA

今天看了一下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(); ) {

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(); ) {

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

繼續閱讀