void HiresTexture::Update() { s_check_native_format = false; s_check_new_format = false; if (s_prefetcher.joinable()) { s_textureCacheAbortLoading.Set(); s_prefetcher.join(); } if (!g_ActiveConfig.bHiresTextures) { s_textureMap.clear(); s_textureCache.clear(); size_sum.store(0); return; } if (!g_ActiveConfig.bCacheHiresTextures) { s_textureCache.clear(); size_sum.store(0); } s_textureMap.clear(); const std::string& gameCode = SConfig::GetInstance().m_strUniqueID; std::string szDir = StringFromFormat("%s%s", File::GetUserPath(D_HIRESTEXTURES_IDX).c_str(), gameCode.c_str()); std::string ddscode(".dds"); std::string cddscode(".DDS"); std::vector<std::string> Extensions = { ".png", ".dds" }; auto rFilenames = DoFileSearch(Extensions, { szDir }, /*recursive*/ true); const std::string code = StringFromFormat("%s_", gameCode.c_str()); const std::string miptag = "mip"; const std::string normaltag = ".nrm"; for (u32 i = 0; i < rFilenames.size(); i++) { std::string FileName; std::string Extension; SplitPath(rFilenames[i], nullptr, &FileName, &Extension); if (FileName.substr(0, code.length()) == code) { s_check_native_format = true; } else if (FileName.substr(0, s_format_prefix.length()) == s_format_prefix) { s_check_new_format = true; } else { // Discard wrong files continue; } const bool is_compressed = Extension.compare(ddscode) == 0 || Extension.compare(cddscode) == 0; const bool is_normal_map = hasEnding(FileName, normaltag); if (is_normal_map) { FileName = FileName.substr(0, FileName.size() - normaltag.size()); } hires_mip_level mip_level_detail(rFilenames[i], Extension, is_compressed); u32 level = 0; size_t idx = FileName.find_last_of('_'); std::string miplevel = FileName.substr(idx + 1, std::string::npos); if (miplevel.substr(0, miptag.length()) == miptag) { sscanf(miplevel.substr(3, std::string::npos).c_str(), "%i", &level); FileName = FileName.substr(0, idx); } HiresTextureCache::iterator iter = s_textureMap.find(FileName); u32 min_item_size = level + 1; if (iter == s_textureMap.end()) { HiresTextureCacheItem item(min_item_size); if (is_normal_map) { item.normal_map.resize(min_item_size); } std::vector<hires_mip_level> &dst = is_normal_map ? item.normal_map : item.color_map; dst[level] = mip_level_detail; s_textureMap.emplace(FileName, item); } else { std::vector<hires_mip_level> &dst = is_normal_map ? iter->second.normal_map : iter->second.color_map; if (dst.size() < min_item_size) { dst.resize(min_item_size); } dst[level] = mip_level_detail; } } if (g_ActiveConfig.bCacheHiresTextures && s_textureMap.size() > 0) { // remove cached but deleted textures auto iter = s_textureCache.begin(); while (iter != s_textureCache.end()) { if (s_textureMap.find(iter->first) == s_textureMap.end()) { size_sum.fetch_sub(iter->second->m_cached_data_size); iter = s_textureCache.erase(iter); } else { iter++; } } s_textureCacheAbortLoading.Clear(); s_prefetcher = std::thread(Prefetch); } }
void HiresTexture::Update() { bool BuildMaterialMaps = g_ActiveConfig.bHiresMaterialMapsBuild; if (s_prefetcher.joinable()) { s_textureCacheAbortLoading.Set(); s_prefetcher.join(); } if (!g_ActiveConfig.bHiresTextures) { s_textureMap.clear(); s_textureCache.clear(); size_sum.store(0); return; } if (!g_ActiveConfig.bCacheHiresTextures) { s_textureCache.clear(); size_sum.store(0); } s_textureMap.clear(); const std::string& game_id = SConfig::GetInstance().GetGameID(); const std::string texture_directory = GetTextureDirectory(game_id); std::string ddscode(".dds"); std::string cddscode(".DDS"); std::vector<std::string> Extensions; Extensions.push_back(".png"); if (!BuildMaterialMaps) { Extensions.push_back(".dds"); } std::vector<std::string> filenames = Common::DoFileSearch({texture_directory}, Extensions, /*recursive*/ true); const std::string miptag = "mip"; for (const std::string& fileitem : filenames) { std::string filename; std::string Extension; SplitPath(fileitem, nullptr, &filename, &Extension); if (filename.substr(0, s_format_prefix.length()) != s_format_prefix) { // Discard wrong files continue; } size_t map_index = 0; size_t max_type = BuildMaterialMaps ? MapType::specular : MapType::normal; bool arbitrary_mips = false; for (size_t tag = 1; tag <= MapType::specular; tag++) { if (StringEndsWith(filename, s_maps_tags[tag])) { map_index = tag; filename = filename.substr(0, filename.size() - s_maps_tags[tag].size()); break; } } if (map_index > max_type) { continue; } if (BuildMaterialMaps && map_index == MapType::material) { continue; } else if (!BuildMaterialMaps && map_index == MapType::color) { const size_t arb_index = filename.rfind("_arb"); arbitrary_mips = arb_index != std::string::npos; if (arbitrary_mips) filename.erase(arb_index, 4); } const bool is_compressed = Extension.compare(ddscode) == 0 || Extension.compare(cddscode) == 0; hires_mip_level mip_level_detail(fileitem, Extension, is_compressed); u32 level = 0; size_t idx = filename.find_last_of('_'); std::string miplevel = filename.substr(idx + 1, std::string::npos); if (miplevel.substr(0, miptag.length()) == miptag) { sscanf(miplevel.substr(3, std::string::npos).c_str(), "%i", &level); filename = filename.substr(0, idx); } HiresTextureCache::iterator iter = s_textureMap.find(filename); u32 min_item_size = level + 1; if (iter == s_textureMap.end()) { HiresTextureCacheItem item(min_item_size); if (arbitrary_mips) { item.has_arbitrary_mips = true; } item.maps[map_index].resize(min_item_size); std::vector<hires_mip_level>& dst = item.maps[map_index]; dst[level] = mip_level_detail; s_textureMap.emplace(filename, item); } else { std::vector<hires_mip_level>& dst = iter->second.maps[map_index]; if (arbitrary_mips) { iter->second.has_arbitrary_mips = true; } if (dst.size() < min_item_size) { dst.resize(min_item_size); } dst[level] = mip_level_detail; } } if (g_ActiveConfig.bCacheHiresTextures && s_textureMap.size() > 0) { // remove cached but deleted textures auto iter = s_textureCache.begin(); while (iter != s_textureCache.end()) { if (s_textureMap.find(iter->first) == s_textureMap.end()) { size_sum.fetch_sub(iter->second->m_cached_data_size); iter = s_textureCache.erase(iter); } else { iter++; } } s_textureCacheAbortLoading.Clear(); s_prefetcher = std::thread(Prefetch); if (g_ActiveConfig.bWaitForCacheHiresTextures && s_prefetcher.joinable()) { s_prefetcher.join(); } } }
std::string HiresTexture::GenBaseName( const u8* texture, size_t texture_size, const u8* tlut, size_t tlut_size, u32 width, u32 height, int format, bool has_mipmaps, bool dump) { std::string name = ""; bool convert = false; HiresTextureCache::iterator convert_iter; if ((!dump || convert) && s_check_native_format) { // try to load the old format first u64 tex_hash = GetHashHiresTexture(texture, (int)texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); u64 tlut_hash = 0; if(tlut_size) tlut_hash = GetHashHiresTexture(tlut, (int)tlut_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); name = StringFromFormat("%s_%08x_%i", SConfig::GetInstance().m_strUniqueID.c_str(), (u32)(tex_hash ^ tlut_hash), (u16)format); convert_iter = s_textureMap.find(name); if (convert_iter != s_textureMap.end()) { if (g_ActiveConfig.bConvertHiresTextures) convert = true; else return name; } } if (dump || s_check_new_format || convert) { // checking for min/max on paletted textures u32 min = 0xffff; u32 max = 0; switch (tlut_size) { case 0: break; case 16 * 2: for (size_t i = 0; i < texture_size; i++) { min = std::min<u32>(min, texture[i] & 0xf); min = std::min<u32>(min, texture[i] >> 4); max = std::max<u32>(max, texture[i] & 0xf); max = std::max<u32>(max, texture[i] >> 4); } break; case 256 * 2: for (size_t i = 0; i < texture_size; i++) { min = std::min<u32>(min, texture[i]); max = std::max<u32>(max, texture[i]); } break; case 16384 * 2: for (size_t i = 0; i < texture_size / 2; i++) { min = std::min<u32>(min, Common::swap16(((u16*)texture)[i]) & 0x3fff); max = std::max<u32>(max, Common::swap16(((u16*)texture)[i]) & 0x3fff); } break; } if (tlut_size > 0) { tlut_size = 2 * (max + 1 - min); tlut += 2 * min; } u64 tex_hash = XXH64(texture, texture_size); u64 tlut_hash = 0; if(tlut_size) tlut_hash = XXH64(tlut, tlut_size); std::string basename = s_format_prefix + StringFromFormat("%dx%d%s_%0016" PRIx64, width, height, has_mipmaps ? "_m" : "", tex_hash); std::string tlutname = tlut_size ? StringFromFormat("_%0016" PRIx64, tlut_hash) : ""; std::string formatname = StringFromFormat("_%d", format); std::string fullname = basename + tlutname + formatname; if (convert) { // new texture if (s_textureMap.find(fullname) == s_textureMap.end()) { HiresTextureCacheItem newitem(convert_iter->second.color_map.size()); for (size_t level = 0; level < convert_iter->second.color_map.size(); level++) { std::string newname = fullname; if (level) newname += StringFromFormat("_mip%d", level); newname += convert_iter->second.color_map[level].extension; std::string &src = convert_iter->second.color_map[level].path; size_t postfix = src.find(name); std::string dst = src.substr(0, postfix) + newname; if (File::Rename(src, dst)) { s_check_new_format = true; OSD::AddMessage(StringFromFormat("Rename custom texture %s to %s", src.c_str(), dst.c_str()), 5000); } else { ERROR_LOG(VIDEO, "rename failed"); } newitem.color_map[level] = hires_mip_level(dst, convert_iter->second.color_map[level].extension, convert_iter->second.color_map[level].is_compressed); } s_textureMap.emplace(fullname, newitem); } else { for (size_t level = 0; level < convert_iter->second.color_map.size(); level++) { if (File::Delete(convert_iter->second.color_map[level].path)) { OSD::AddMessage(StringFromFormat("Delete double old custom texture %s", convert_iter->second.color_map[level].path.c_str()), 5000); } else { ERROR_LOG(VIDEO, "delete failed"); } } } s_textureMap.erase(name); } return fullname; }