0
0

More than 5 years have passed since last update.

Cocos2d-xでpurgeCachedDataをやったら文字が抜ける

Last updated at Posted at 2017-03-15

概要

  • Cocos2d-xのCCDirector.cppの void Director::purgeCachedData(void) を行うとLabelで描画した文字が抜けることがある

バージョン情報

  • Cocos2d-x v3.2.0
  • 現在の最新バージョンv3.14.1でも未修正だったのでおそらくこの現象は起こる

原因概要

Director::purgeCachedData() 内の処理に問題あり。

CCDirector.cpp
void Director::purgeCachedData(void)
{
    FontFNT::purgeCachedData();
    FontAtlasCache::purgeCachedData();

    if (s_SharedDirector->getOpenGLView())
    {
        SpriteFrameCache::getInstance()->removeUnusedSpriteFrames();
        _textureCache->removeUnusedTextures();

        // Note: some tests such as ActionsTest are leaking refcounted textures
        // There should be no test textures left in the cache
        CCLOG("%s\n", _textureCache->getCachedTextureInfo().c_str());
    }
    FileUtils::getInstance()->purgeCachedEntries();
}

原因詳細

Director::purgeCachedData(void)FontAtlasCache::purgeCachedData() が呼ばれる。
FontAtlasCache::purgeCachedData() でFontAtlasの purgeTexturesAtlas() が呼ばれる。

CCFontAtlas.cpp
void FontAtlas::purgeTexturesAtlas()
{
    FontFreeType* fontTTf = dynamic_cast<FontFreeType*>(_font);
    if (fontTTf && _atlasTextures.size() > 1)
    {
        auto eventDispatcher = Director::getInstance()->getEventDispatcher();
        eventDispatcher->dispatchCustomEvent(CMD_PURGE_FONTATLAS,this);
        eventDispatcher->dispatchCustomEvent(CMD_RESET_FONTATLAS,this);
    }
}

ここでCMD_PURGE_FONTATLASとCMD_RESET_FONTATLASのイベントが同時に発行される。
この2つはどこでもセットで呼ばれている。
このイベントを受け取っているのはCCLabelクラス。

CMD_PURGE_FONTATLASのイベントで、SpriteBatchNodeをclearしている。
_fontAtlasも解放している。

その後、CMD_RESET_FONTATLASで_fontAtlasがnullptrになった状態で
setTTFCongig(_fontConfig)を行う処理があるが、
この時点で_fontAtlasがnullptrになる場合があり、結果その処理に行かず、リセットされない。
結果、_fontAtlasと_atlasMapに不整合が生じ、文字が再描画されない(特定の文字が解放されっぱなしになっている)。

CCLabel.cpp
 _purgeTextureListener = EventListenerCustom::create(FontAtlas::CMD_PURGE_FONTATLAS, [this](EventCustom* event){
        if (_fontAtlas && _currentLabelType == LabelType::TTF && event->getUserData() == _fontAtlas)
        {
            for (auto&& it : _letters)
            {
                it.second->setTexture(nullptr);
            }
            _batchNodes.clear();

            // _fontAtlasが解放される
            if (_fontAtlas)
            {
                FontAtlasCache::releaseFontAtlas(_fontAtlas);
            }
        }
    });
    _eventDispatcher->addEventListenerWithFixedPriority(_purgeTextureListener, 1);

    _resetTextureListener = EventListenerCustom::create(FontAtlas::CMD_RESET_FONTATLAS, [this](EventCustom* event){
        // この時点で_fontAtlasがnullptrになっている場合はifに入らない
        if (_fontAtlas && _currentLabelType == LabelType::TTF && event->getUserData() == _fontAtlas)
        {
            _fontAtlas = nullptr;
            this->setTTFConfig(_fontConfig);
            for (auto&& it : _letters)
            {
                getLetter(it.first);
            }
        }
    });

修正

  • CMD_RESET_FONTATLASイベントで_fontAtlasを解放するようにした
CCLabel.cpp
_purgeTextureListener = EventListenerCustom::create(FontAtlas::CMD_PURGE_FONTATLAS, [this](EventCustom* event){
        if (_fontAtlas && _currentLabelType == LabelType::TTF && event->getUserData() == _fontAtlas)
        {
            for (auto&& it : _letters)
            {
                it.second->setTexture(nullptr);
            }
            _batchNodes.clear();
        }
    });
    _eventDispatcher->addEventListenerWithFixedPriority(_purgeTextureListener, 1);

    _resetTextureListener = EventListenerCustom::create(FontAtlas::CMD_RESET_FONTATLAS, [this](EventCustom* event){
        if (_fontAtlas && _currentLabelType == LabelType::TTF && event->getUserData() == _fontAtlas)
        {
            // ここで明確にreleaseしてnullptrする
            CC_SAFE_RELEASE_NULL(_fontAtlas);

            this->setTTFConfig(_fontConfig);
            for (auto&& it : _letters)
            {
                getLetter(it.first);
            }
        }
    });
    _eventDispatcher->addEventListenerWithFixedPriority(_resetTextureListener, 2);
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0