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 "";
}
测试结果
-
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_ARCHIVE,isFileExistInternal直接返回了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
原始的win32项目填写了Command Arguments,所以规避了这个问题
结论
SearchPath的影响还是非常大的,写资源路径的时候,最好统一相对于一个目录写,不然cache中容易出现多份相同的资源。