CustomTexture: prefetch all available textures
This commit is contained in:
parent
7e6ec5fa86
commit
242f7d964d
|
@ -130,6 +130,7 @@ static wxString xfb_virtual_desc = _("Emulate XFBs using GPU texture objects.\nF
|
|||
static wxString xfb_real_desc = _("Emulate XFBs accurately.\nSlows down emulation a lot and prohibits high-resolution rendering but is necessary to emulate a number of games properly.\n\nIf unsure, check virtual XFB emulation instead.");
|
||||
static wxString dump_textures_desc = _("Dump decoded game textures to User/Dump/Textures/<game_id>/.\n\nIf unsure, leave this unchecked.");
|
||||
static wxString load_hires_textures_desc = _("Load custom textures from User/Load/Textures/<game_id>/.\n\nIf unsure, leave this unchecked.");
|
||||
static wxString cache_hires_textures_desc = _("Cache custom textures to system RAM on startup.\nThis can require exponentially more RAM but fixes possible stuttering.\n\nIf unsure, leave this unchecked.");
|
||||
static wxString dump_efb_desc = _("Dump the contents of EFB copies to User/Dump/Textures/.\n\nIf unsure, leave this unchecked.");
|
||||
#if !defined WIN32 && defined HAVE_LIBAV
|
||||
static wxString use_ffv1_desc = _("Encode frame dumps using the FFV1 codec.\n\nIf unsure, leave this unchecked.");
|
||||
|
@ -554,6 +555,8 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
|
|||
|
||||
szr_utility->Add(CreateCheckBox(page_advanced, _("Dump Textures"), dump_textures_desc, vconfig.bDumpTextures));
|
||||
szr_utility->Add(CreateCheckBox(page_advanced, _("Load Custom Textures"), load_hires_textures_desc, vconfig.bHiresTextures));
|
||||
cache_hires_textures = CreateCheckBox(page_advanced, _("Prefetch Custom Textures"), cache_hires_textures_desc, vconfig.bCacheHiresTextures);
|
||||
szr_utility->Add(cache_hires_textures);
|
||||
szr_utility->Add(CreateCheckBox(page_advanced, _("Dump EFB Target"), dump_efb_desc, vconfig.bDumpEFBTarget));
|
||||
szr_utility->Add(CreateCheckBox(page_advanced, _("Free Look"), free_look_desc, vconfig.bFreeLook));
|
||||
#if !defined WIN32 && defined HAVE_LIBAV
|
||||
|
|
|
@ -199,6 +199,9 @@ protected:
|
|||
virtual_xfb->Enable(vconfig.bUseXFB);
|
||||
real_xfb->Enable(vconfig.bUseXFB);
|
||||
|
||||
// custom textures
|
||||
cache_hires_textures->Enable(vconfig.bHiresTextures);
|
||||
|
||||
// Repopulating the post-processing shaders can't be done from an event
|
||||
if (choice_ppshader && choice_ppshader->IsEmpty())
|
||||
PopulatePostProcessingShaders();
|
||||
|
@ -262,6 +265,8 @@ protected:
|
|||
SettingRadioButton* virtual_xfb;
|
||||
SettingRadioButton* real_xfb;
|
||||
|
||||
SettingCheckBox* cache_hires_textures;
|
||||
|
||||
wxCheckBox* progressive_scan_checkbox;
|
||||
|
||||
wxChoice* choice_ppshader;
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <xxhash.h>
|
||||
#include <SOIL/SOIL.h>
|
||||
|
@ -13,7 +15,10 @@
|
|||
#include "Common/CommonPaths.h"
|
||||
#include "Common/FileSearch.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Flag.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "Common/Timer.h"
|
||||
|
||||
#include "Core/ConfigManager.h"
|
||||
|
||||
|
@ -22,18 +27,59 @@
|
|||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
static std::unordered_map<std::string, std::string> s_textureMap;
|
||||
static std::unordered_map<std::string, std::shared_ptr<HiresTexture>> s_textureCache;
|
||||
static std::mutex s_textureCacheMutex;
|
||||
static std::mutex s_textureCacheAquireMutex; // for high priority access
|
||||
static Common::Flag s_textureCacheAbortLoading;
|
||||
static bool s_check_native_format;
|
||||
static bool s_check_new_format;
|
||||
|
||||
static std::thread s_prefetcher;
|
||||
|
||||
static const std::string s_format_prefix = "tex1_";
|
||||
|
||||
void HiresTexture::Init(const std::string& gameCode)
|
||||
void HiresTexture::Init()
|
||||
{
|
||||
s_textureMap.clear();
|
||||
s_check_native_format = false;
|
||||
s_check_new_format = false;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
void HiresTexture::Shutdown()
|
||||
{
|
||||
if (s_prefetcher.joinable())
|
||||
{
|
||||
s_textureCacheAbortLoading.Set();
|
||||
s_prefetcher.join();
|
||||
}
|
||||
|
||||
s_textureMap.clear();
|
||||
s_textureCache.clear();
|
||||
}
|
||||
|
||||
void HiresTexture::Update()
|
||||
{
|
||||
if (s_prefetcher.joinable())
|
||||
{
|
||||
s_textureCacheAbortLoading.Set();
|
||||
s_prefetcher.join();
|
||||
}
|
||||
|
||||
if (!g_ActiveConfig.bHiresTextures)
|
||||
{
|
||||
s_textureMap.clear();
|
||||
s_textureCache.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_ActiveConfig.bCacheHiresTextures)
|
||||
{
|
||||
s_textureCache.clear();
|
||||
}
|
||||
|
||||
CFileSearch::XStringVector Directories;
|
||||
const std::string& gameCode = SConfig::GetInstance().m_LocalCoreStartupParameter.m_strUniqueID;
|
||||
|
||||
std::string szDir = StringFromFormat("%s%s", File::GetUserPath(D_HIRESTEXTURES_IDX).c_str(), gameCode.c_str());
|
||||
Directories.push_back(szDir);
|
||||
|
@ -94,6 +140,72 @@ void HiresTexture::Init(const std::string& gameCode)
|
|||
s_check_new_format = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.bCacheHiresTextures)
|
||||
{
|
||||
// remove cached but deleted textures
|
||||
auto iter = s_textureCache.begin();
|
||||
while (iter != s_textureCache.end())
|
||||
{
|
||||
if (s_textureMap.find(iter->first) == s_textureMap.end())
|
||||
{
|
||||
iter = s_textureCache.erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
s_textureCacheAbortLoading.Clear();
|
||||
s_prefetcher = std::thread(Prefetch);
|
||||
}
|
||||
}
|
||||
|
||||
void HiresTexture::Prefetch()
|
||||
{
|
||||
Common::SetCurrentThreadName("Prefetcher");
|
||||
|
||||
size_t size_sum = 0;
|
||||
u32 starttime = Common::Timer::GetTimeMs();
|
||||
for (const auto& entry : s_textureMap)
|
||||
{
|
||||
const std::string& base_filename = entry.first;
|
||||
|
||||
if (base_filename.find("_mip") == std::string::npos)
|
||||
{
|
||||
{
|
||||
// try to get this mutex first, so the video thread is allow to get the real mutex faster
|
||||
std::unique_lock<std::mutex> lk(s_textureCacheAquireMutex);
|
||||
}
|
||||
std::unique_lock<std::mutex> lk(s_textureCacheMutex);
|
||||
|
||||
auto iter = s_textureCache.find(base_filename);
|
||||
if (iter == s_textureCache.end())
|
||||
{
|
||||
// unlock while loading a texture. This may result in a race condition where we'll load a texture twice,
|
||||
// but it reduces the stuttering a lot. Notice: The loading library _must_ be thread safe now.
|
||||
// But bad luck, SOIL isn't, so TODO: remove SOIL usage here and use libpng directly
|
||||
// Also TODO: remove s_textureCacheAquireMutex afterwards. It won't be needed as the main mutex will be locked rarely
|
||||
//lk.unlock();
|
||||
std::shared_ptr<HiresTexture> ptr(Load(base_filename, 0, 0));
|
||||
//lk.lock();
|
||||
|
||||
iter = s_textureCache.insert(iter, std::make_pair(base_filename, ptr));
|
||||
}
|
||||
for (const Level& l : iter->second->m_levels)
|
||||
{
|
||||
size_sum += l.data_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (s_textureCacheAbortLoading.IsSet())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
u32 stoptime = Common::Timer::GetTimeMs();
|
||||
OSD::AddMessage(StringFromFormat("Custom Textures loaded, %.1f MB in %.1f s", size_sum / (1024.0 * 1024.0), (stoptime - starttime) / 1000.0), 10000);
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -238,10 +350,31 @@ std::string HiresTexture::GenBaseName(const u8* texture, size_t texture_size, co
|
|||
return name;
|
||||
}
|
||||
|
||||
HiresTexture* HiresTexture::Search(const u8* texture, size_t texture_size, const u8* tlut, size_t tlut_size, u32 width, u32 height, int format, bool has_mipmaps)
|
||||
std::shared_ptr<HiresTexture> HiresTexture::Search(const u8* texture, size_t texture_size, const u8* tlut, size_t tlut_size, u32 width, u32 height, int format, bool has_mipmaps)
|
||||
{
|
||||
std::string base_filename = GenBaseName(texture, texture_size, tlut, tlut_size, width, height, format, has_mipmaps);
|
||||
|
||||
std::lock_guard<std::mutex> lk2(s_textureCacheAquireMutex);
|
||||
std::lock_guard<std::mutex> lk(s_textureCacheMutex);
|
||||
|
||||
auto iter = s_textureCache.find(base_filename);
|
||||
if (iter != s_textureCache.end())
|
||||
{
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
std::shared_ptr<HiresTexture> ptr(Load(base_filename, width, height));
|
||||
|
||||
if (ptr && g_ActiveConfig.bCacheHiresTextures)
|
||||
{
|
||||
s_textureCache[base_filename] = ptr;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
HiresTexture* HiresTexture::Load(const std::string& base_filename, u32 width, u32 height)
|
||||
{
|
||||
HiresTexture* ret = nullptr;
|
||||
for (int level = 0;; level++)
|
||||
{
|
||||
|
@ -275,7 +408,7 @@ HiresTexture* HiresTexture::Search(const u8* texture, size_t texture_size, const
|
|||
if (l.width * height != l.height * width)
|
||||
ERROR_LOG(VIDEO, "Invalid custom texture size %dx%d for texture %s. The aspect differs from the native size %dx%d.",
|
||||
l.width, l.height, filename.c_str(), width, height);
|
||||
if (l.width % width || l.height % height)
|
||||
if (width && height && (l.width % width || l.height % height))
|
||||
WARN_LOG(VIDEO, "Invalid custom texture size %dx%d for texture %s. Please use an integer upscaling factor based on the native size %dx%d.",
|
||||
l.width, l.height, filename.c_str(), width, height);
|
||||
width = l.width;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "VideoCommon/TextureDecoder.h"
|
||||
|
@ -12,9 +13,11 @@
|
|||
class HiresTexture
|
||||
{
|
||||
public:
|
||||
static void Init(const std::string& gameCode);
|
||||
static void Init();
|
||||
static void Update();
|
||||
static void Shutdown();
|
||||
|
||||
static HiresTexture* Search(
|
||||
static std::shared_ptr<HiresTexture> Search(
|
||||
const u8* texture, size_t texture_size,
|
||||
const u8* tlut, size_t tlut_size,
|
||||
u32 width, u32 height,
|
||||
|
@ -40,6 +43,9 @@ public:
|
|||
std::vector<Level> m_levels;
|
||||
|
||||
private:
|
||||
static HiresTexture* Load(const std::string& base_filename, u32 width, u32 height);
|
||||
static void Prefetch();
|
||||
|
||||
HiresTexture() {}
|
||||
|
||||
};
|
||||
|
|
|
@ -60,8 +60,7 @@ TextureCache::TextureCache()
|
|||
|
||||
TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter);
|
||||
|
||||
if (g_ActiveConfig.bHiresTextures && !g_ActiveConfig.bDumpTextures)
|
||||
HiresTexture::Init(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strUniqueID);
|
||||
HiresTexture::Init();
|
||||
|
||||
SetHash64Function();
|
||||
|
||||
|
@ -92,6 +91,7 @@ void TextureCache::Invalidate()
|
|||
|
||||
TextureCache::~TextureCache()
|
||||
{
|
||||
HiresTexture::Shutdown();
|
||||
Invalidate();
|
||||
FreeAlignedMemory(temp);
|
||||
temp = nullptr;
|
||||
|
@ -101,6 +101,12 @@ void TextureCache::OnConfigChanged(VideoConfig& config)
|
|||
{
|
||||
if (g_texture_cache)
|
||||
{
|
||||
if (config.bHiresTextures != backup_config.s_hires_textures ||
|
||||
config.bCacheHiresTextures != backup_config.s_cache_hires_textures)
|
||||
{
|
||||
HiresTexture::Update();
|
||||
}
|
||||
|
||||
// TODO: Invalidating texcache is really stupid in some of these cases
|
||||
if (config.iSafeTextureCache_ColorSamples != backup_config.s_colorsamples ||
|
||||
config.bTexFmtOverlayEnable != backup_config.s_texfmt_overlay ||
|
||||
|
@ -110,9 +116,6 @@ void TextureCache::OnConfigChanged(VideoConfig& config)
|
|||
{
|
||||
g_texture_cache->Invalidate();
|
||||
|
||||
if (g_ActiveConfig.bHiresTextures)
|
||||
HiresTexture::Init(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strUniqueID);
|
||||
|
||||
TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter);
|
||||
|
||||
invalidate_texture_cache_requested = false;
|
||||
|
@ -130,6 +133,7 @@ void TextureCache::OnConfigChanged(VideoConfig& config)
|
|||
backup_config.s_texfmt_overlay = config.bTexFmtOverlayEnable;
|
||||
backup_config.s_texfmt_overlay_center = config.bTexFmtOverlayCenter;
|
||||
backup_config.s_hires_textures = config.bHiresTextures;
|
||||
backup_config.s_cache_hires_textures = config.bCacheHiresTextures;
|
||||
backup_config.s_stereo_3d = config.iStereoMode > 0;
|
||||
backup_config.s_efb_mono_depth = config.bStereoEFBMonoDepth;
|
||||
}
|
||||
|
@ -433,15 +437,15 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage)
|
|||
textures.erase(oldest_entry);
|
||||
}
|
||||
|
||||
std::unique_ptr<HiresTexture> hires_tex;
|
||||
std::shared_ptr<HiresTexture> hires_tex;
|
||||
if (g_ActiveConfig.bHiresTextures)
|
||||
{
|
||||
hires_tex.reset(HiresTexture::Search(
|
||||
hires_tex = HiresTexture::Search(
|
||||
src_data, texture_size,
|
||||
&texMem[tlutaddr], palette_size,
|
||||
width, height,
|
||||
texformat, use_mipmaps
|
||||
));
|
||||
);
|
||||
|
||||
if (hires_tex)
|
||||
{
|
||||
|
|
|
@ -153,6 +153,7 @@ private:
|
|||
bool s_texfmt_overlay;
|
||||
bool s_texfmt_overlay_center;
|
||||
bool s_hires_textures;
|
||||
bool s_cache_hires_textures;
|
||||
bool s_copy_cache_enable;
|
||||
bool s_stereo_3d;
|
||||
bool s_efb_mono_depth;
|
||||
|
|
|
@ -71,6 +71,7 @@ void VideoConfig::Load(const std::string& ini_file)
|
|||
settings->Get("DumpTextures", &bDumpTextures, 0);
|
||||
settings->Get("HiresTextures", &bHiresTextures, 0);
|
||||
settings->Get("ConvertHiresTextures", &bConvertHiresTextures, 0);
|
||||
settings->Get("CacheHiresTextures", &bCacheHiresTextures, 0);
|
||||
settings->Get("DumpEFBTarget", &bDumpEFBTarget, 0);
|
||||
settings->Get("FreeLook", &bFreeLook, 0);
|
||||
settings->Get("UseFFV1", &bUseFFV1, 0);
|
||||
|
@ -150,6 +151,7 @@ void VideoConfig::GameIniLoad()
|
|||
CHECK_SETTING("Video_Settings", "SafeTextureCacheColorSamples", iSafeTextureCache_ColorSamples);
|
||||
CHECK_SETTING("Video_Settings", "HiresTextures", bHiresTextures);
|
||||
CHECK_SETTING("Video_Settings", "ConvertHiresTextures", bConvertHiresTextures);
|
||||
CHECK_SETTING("Video_Settings", "CacheHiresTextures", bCacheHiresTextures);
|
||||
CHECK_SETTING("Video_Settings", "EnablePixelLighting", bEnablePixelLighting);
|
||||
CHECK_SETTING("Video_Settings", "FastDepthCalc", bFastDepthCalc);
|
||||
CHECK_SETTING("Video_Settings", "MSAA", iMultisampleMode);
|
||||
|
@ -256,6 +258,7 @@ void VideoConfig::Save(const std::string& ini_file)
|
|||
settings->Set("DumpTextures", bDumpTextures);
|
||||
settings->Set("HiresTextures", bHiresTextures);
|
||||
settings->Set("ConvertHiresTextures", bConvertHiresTextures);
|
||||
settings->Set("CacheHiresTextures", bCacheHiresTextures);
|
||||
settings->Set("DumpEFBTarget", bDumpEFBTarget);
|
||||
settings->Set("FreeLook", bFreeLook);
|
||||
settings->Set("UseFFV1", bUseFFV1);
|
||||
|
|
|
@ -104,6 +104,7 @@ struct VideoConfig final
|
|||
bool bDumpTextures;
|
||||
bool bHiresTextures;
|
||||
bool bConvertHiresTextures;
|
||||
bool bCacheHiresTextures;
|
||||
bool bDumpEFBTarget;
|
||||
bool bUseFFV1;
|
||||
bool bFreeLook;
|
||||
|
|
Loading…
Reference in New Issue