Cocos2dx的cache细节,资源路径不规范,容易导致cache中存在多份

104 阅读2分钟

SpriteFrameCache

SpriteFrameCache::getInstance()->addSpriteFramesWithFile("");

TextureCache

std::unordered_map<std::string, TexInfo*> _textures;

key的来源

Texture2D * TextureCache::addImage(const std::string &path){
    std::string fullpath = GetFullPath(path);
    auto it = _textures.find(fullpath);
}

std::string GetFullPath(std::string path){
    std::string fullPath = FileUtils::getInstance()->fullPathForFilename(path);
    if (fullPath.length() > 0){
        return fullPath;
    }else{
        return path;
    }
}
 
std::string FileUtils::fullPathForFilename(const std::string &filename) const
{
    if (path[0] == '/')
    {
        return filename;
    }
    std::string name = filename;
    int index = name.find("/../");
    while (index > 0) {
            int last = name.find_last_of("/", index-1);
            if (last > 0) {
                    name = name.substr(0, last) + name.substr(index+3, name.length());
            }
            else {
                    break;
            }
            index = name.find("/../");
    }
    // Already Cached ?
    auto cacheIter = _fullPathCache.find(name);
    // 又一个很重要的cache: mutable std::unordered_map<std::string, std::string> _fullPathCache;
    if(cacheIter != _fullPathCache.end())
    {
        return cacheIter->second;
    }
    // Get the new file name.
    const std::string newFilename(getNewFilename(name));
    std::string fullpath;
    for (const auto& searchIt : _searchPathArray){
        for (const auto& resolutionIt : _searchResolutionsOrderArray){
            fullpath = this->getPathForFilename(newFilename, resolutionIt, searchIt);
            if (!fullpath.empty()){
                // Using the filename passed in as key.
                _fullPathCache.emplace(name, fullpath);
                return fullpath;
            }
        }
    }
    return "";
}

测试结果

image.png

  • name为ani1/test.png,在textureCache中的映射key为res/ani1/test.png

  • name为res/ani1/test.png,在textureCache中的映射key为exePath/res/ani1/test.png

相当于加载的是磁盘的同一份图片,结果却产生了2个纹理,理想情况是产生1份,产生这个问题的原因是:

TextureCache的key受filePathCache的影响,而filePathCache受searchPath的影响

searchPath的目录的顺序为:

  • exePath
  • src
  • res

ani1/test.png只能在res目录下找到,res/ani1/test.png只能在exePath目录下找到,所以最终2者的key是完全不同的。

造成这个问题是因为我设置了一个无效的searchPath,而在win32平台判断文件是否存在发生了异常

bool FileUtilsWin32::isFileExistInternal(const std::string& strFilePath) const
{
    if (strFilePath.empty())
    {
        return false;
    }

    std::string strPath = strFilePath;
    if (!isAbsolutePath(strPath))
    { // Not absolute path, add the default root path at the beginning.
        strPath.insert(0, _defaultResRootPath);
    }

    DWORD attr = GetFileAttributesW(StringUtf8ToWideChar(strPath).c_str());
    if(attr == INVALID_FILE_ATTRIBUTES || (attr & FILE_ATTRIBUTE_DIRECTORY))
        return false;   //  not a file
    return true;
}

当strFile为res/ani1/test.png返回的是FILE_ATTRIBUTE_ARCHIVEisFileExistInternal直接返回了true,认为存在,准确说这个文件是相对于exe是存在的,但是这个路径不是完整路径

  • 测试代码:
char* file = "res/ani2/test.png"; // exe运行目录下真的有这个文件
//file = "c://a/1.png";
DWORD attr = GetFileAttributesW(StringUtf8ToWideChar(file).c_str());
if (attr == INVALID_FILE_ATTRIBUTES || (attr & FILE_ATTRIBUTE_DIRECTORY))
{
    cocos2d::log("file not exist");
}
else{
    cocos2d::log("file exist");
}

为什么会出现search paths异常

FileUtils::getInstance()->addSearchPath("res");
void FileUtils::addSearchPath(const std::string &searchpath,const bool front)
{
    if (!isAbsolutePath(searchpath))
        prefix = _defaultResRootPath;
}

当添加searchPaths的时候,如果是相对路径,则会追加_defaultResRootPath,而_defaultResRootPath在win32上的来源是ProjectConfig,在SimulatorWin::run中就会从命令行参数中解析workdir最终传递给_defaultResRootPath

void FileUtils::setDefaultResourceRootPath(const std::string& path)
{
    // 最主要的原因是这里缺少对path的校验
    if (_defaultResRootPath != path)
    {
        _fullPathCache.clear();
        _defaultResRootPath = path;
        if (!_defaultResRootPath.empty() && _defaultResRootPath[_defaultResRootPath.length()-1] != '/')
        {
            _defaultResRootPath += '/';
        }

        // Updates search paths
        setSearchPaths(_originalSearchPaths);
    }
}

release出现问题就是因为args中缺少workdir

image.png 原始的win32项目填写了Command Arguments,所以规避了这个问题

结论

SearchPath的影响还是非常大的,写资源路径的时候,最好统一相对于一个目录写,不然cache中容易出现多份相同的资源。