mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-04-20 11:35:54 +00:00
VideoCommon: add resource manager and new asset loader; the resource manager uses a least recently used cache to determine which assets get priority for loading. Additionally, if the system is low on memory, assets will be purged with the less requested assets being the first to go. The loader is multithreaded now and loads assets as quickly as possible as long as memory is available
This commit is contained in:
parent
07b4c53371
commit
4489a30d4e
6 changed files with 632 additions and 0 deletions
|
@ -659,6 +659,8 @@
|
|||
<ClInclude Include="VideoCommon\Assets\CustomAsset.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\CustomAssetLibrary.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\CustomAssetLoader.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\CustomAssetLoader2.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\CustomResourceManager.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\CustomTextureData.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\DirectFilesystemAssetLibrary.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\MaterialAsset.h" />
|
||||
|
@ -1306,6 +1308,8 @@
|
|||
<ClCompile Include="VideoCommon\Assets\CustomAsset.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\CustomAssetLibrary.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\CustomAssetLoader.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\CustomAssetLoader2.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\CustomResourceManager.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\CustomTextureData.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\DirectFilesystemAssetLibrary.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\MaterialAsset.cpp" />
|
||||
|
|
175
Source/Core/VideoCommon/Assets/CustomAssetLoader2.cpp
Normal file
175
Source/Core/VideoCommon/Assets/CustomAssetLoader2.cpp
Normal file
|
@ -0,0 +1,175 @@
|
|||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/Assets/CustomAssetLoader2.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/Thread.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
void CustomAssetLoader2::Initialize()
|
||||
{
|
||||
ResizeWorkerThreads(2);
|
||||
}
|
||||
|
||||
void CustomAssetLoader2 ::Shutdown()
|
||||
{
|
||||
Reset(false);
|
||||
}
|
||||
|
||||
void CustomAssetLoader2::Reset(bool restart_worker_threads)
|
||||
{
|
||||
const std::size_t worker_thread_count = m_worker_threads.size();
|
||||
StopWorkerThreads();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_pending_work_lock);
|
||||
m_pending_assets.clear();
|
||||
m_max_memory_allowed = 0;
|
||||
m_current_asset_memory = 0;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_completed_work_lock);
|
||||
m_completed_asset_session_ids.clear();
|
||||
m_completed_asset_memory = 0;
|
||||
}
|
||||
|
||||
if (restart_worker_threads)
|
||||
{
|
||||
StartWorkerThreads(static_cast<u32>(worker_thread_count));
|
||||
}
|
||||
}
|
||||
|
||||
bool CustomAssetLoader2::StartWorkerThreads(u32 num_worker_threads)
|
||||
{
|
||||
if (num_worker_threads == 0)
|
||||
return true;
|
||||
|
||||
for (u32 i = 0; i < num_worker_threads; i++)
|
||||
{
|
||||
m_worker_thread_start_result.store(false);
|
||||
|
||||
void* thread_param = nullptr;
|
||||
std::thread thr(&CustomAssetLoader2::WorkerThreadEntryPoint, this, thread_param);
|
||||
m_init_event.Wait();
|
||||
|
||||
if (!m_worker_thread_start_result.load())
|
||||
{
|
||||
WARN_LOG_FMT(VIDEO, "Failed to start asset load worker thread.");
|
||||
thr.join();
|
||||
break;
|
||||
}
|
||||
|
||||
m_worker_threads.push_back(std::move(thr));
|
||||
}
|
||||
|
||||
return HasWorkerThreads();
|
||||
}
|
||||
|
||||
bool CustomAssetLoader2::ResizeWorkerThreads(u32 num_worker_threads)
|
||||
{
|
||||
if (m_worker_threads.size() == num_worker_threads)
|
||||
return true;
|
||||
|
||||
StopWorkerThreads();
|
||||
return StartWorkerThreads(num_worker_threads);
|
||||
}
|
||||
|
||||
bool CustomAssetLoader2::HasWorkerThreads() const
|
||||
{
|
||||
return !m_worker_threads.empty();
|
||||
}
|
||||
|
||||
void CustomAssetLoader2::StopWorkerThreads()
|
||||
{
|
||||
if (!HasWorkerThreads())
|
||||
return;
|
||||
|
||||
// Signal worker threads to stop, and wake all of them.
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_pending_work_lock);
|
||||
m_exit_flag.Set();
|
||||
m_worker_thread_wake.notify_all();
|
||||
}
|
||||
|
||||
// Wait for worker threads to exit.
|
||||
for (std::thread& thr : m_worker_threads)
|
||||
thr.join();
|
||||
m_worker_threads.clear();
|
||||
m_exit_flag.Clear();
|
||||
}
|
||||
|
||||
void CustomAssetLoader2::WorkerThreadEntryPoint(void* param)
|
||||
{
|
||||
Common::SetCurrentThreadName("Asset Loader Worker");
|
||||
|
||||
m_worker_thread_start_result.store(true);
|
||||
m_init_event.Set();
|
||||
|
||||
WorkerThreadRun();
|
||||
}
|
||||
|
||||
void CustomAssetLoader2::WorkerThreadRun()
|
||||
{
|
||||
std::unique_lock<std::mutex> pending_lock(m_pending_work_lock);
|
||||
while (!m_exit_flag.IsSet())
|
||||
{
|
||||
m_worker_thread_wake.wait(pending_lock);
|
||||
|
||||
while (!m_pending_assets.empty() && !m_exit_flag.IsSet())
|
||||
{
|
||||
auto pending_iter = m_pending_assets.begin();
|
||||
const auto item = *pending_iter;
|
||||
m_pending_assets.erase(pending_iter);
|
||||
|
||||
if ((m_current_asset_memory + m_completed_asset_memory) > m_max_memory_allowed)
|
||||
break;
|
||||
|
||||
pending_lock.unlock();
|
||||
if (item->Load())
|
||||
{
|
||||
std::lock_guard<std::mutex> completed_guard(m_completed_work_lock);
|
||||
m_completed_asset_memory += item->GetByteSizeInMemory();
|
||||
m_completed_asset_session_ids.push_back(item->GetSessionId());
|
||||
}
|
||||
|
||||
pending_lock.lock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::size_t>
|
||||
CustomAssetLoader2::LoadAssets(const std::list<CustomAsset*>& pending_assets,
|
||||
u64 current_loaded_memory, u64 max_memory_allowed)
|
||||
{
|
||||
u64 total_memory = current_loaded_memory;
|
||||
std::vector<std::size_t> completed_asset_session_ids;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_completed_work_lock);
|
||||
m_completed_asset_session_ids.swap(completed_asset_session_ids);
|
||||
total_memory += m_completed_asset_memory;
|
||||
m_completed_asset_memory = 0;
|
||||
}
|
||||
|
||||
if (pending_assets.empty())
|
||||
return completed_asset_session_ids;
|
||||
|
||||
if (total_memory > max_memory_allowed)
|
||||
return completed_asset_session_ids;
|
||||
|
||||
// There's new assets to process, notify worker threads
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_pending_work_lock);
|
||||
m_pending_assets = pending_assets;
|
||||
m_current_asset_memory = total_memory;
|
||||
m_max_memory_allowed = max_memory_allowed;
|
||||
if (m_current_asset_memory < m_max_memory_allowed)
|
||||
m_worker_thread_wake.notify_all();
|
||||
}
|
||||
|
||||
return completed_asset_session_ids;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
65
Source/Core/VideoCommon/Assets/CustomAssetLoader2.h
Normal file
65
Source/Core/VideoCommon/Assets/CustomAssetLoader2.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/Event.h"
|
||||
#include "Common/Flag.h"
|
||||
#include "VideoCommon/Assets/CustomAsset.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
class CustomAssetLoader2
|
||||
{
|
||||
public:
|
||||
CustomAssetLoader2() = default;
|
||||
~CustomAssetLoader2() = default;
|
||||
CustomAssetLoader2(const CustomAssetLoader2&) = delete;
|
||||
CustomAssetLoader2(CustomAssetLoader2&&) = delete;
|
||||
CustomAssetLoader2& operator=(const CustomAssetLoader2&) = delete;
|
||||
CustomAssetLoader2& operator=(CustomAssetLoader2&&) = delete;
|
||||
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
|
||||
// Returns a vector of asset session ids that were loaded in the last frame
|
||||
std::vector<std::size_t> LoadAssets(const std::list<CustomAsset*>& pending_assets,
|
||||
u64 current_loaded_memory, u64 max_memory_allowed);
|
||||
|
||||
void Reset(bool restart_worker_threads = true);
|
||||
|
||||
private:
|
||||
bool StartWorkerThreads(u32 num_worker_threads);
|
||||
bool ResizeWorkerThreads(u32 num_worker_threads);
|
||||
bool HasWorkerThreads() const;
|
||||
void StopWorkerThreads();
|
||||
|
||||
void WorkerThreadEntryPoint(void* param);
|
||||
void WorkerThreadRun();
|
||||
|
||||
Common::Flag m_exit_flag;
|
||||
Common::Event m_init_event;
|
||||
|
||||
std::vector<std::thread> m_worker_threads;
|
||||
std::atomic_bool m_worker_thread_start_result{false};
|
||||
|
||||
std::list<CustomAsset*> m_pending_assets;
|
||||
std::atomic<u64> m_current_asset_memory = 0;
|
||||
u64 m_max_memory_allowed = 0;
|
||||
std::mutex m_pending_work_lock;
|
||||
|
||||
std::condition_variable m_worker_thread_wake;
|
||||
|
||||
std::vector<std::size_t> m_completed_asset_session_ids;
|
||||
std::atomic<u64> m_completed_asset_memory = 0;
|
||||
std::mutex m_completed_work_lock;
|
||||
};
|
||||
} // namespace VideoCommon
|
202
Source/Core/VideoCommon/Assets/CustomResourceManager.cpp
Normal file
202
Source/Core/VideoCommon/Assets/CustomResourceManager.cpp
Normal file
|
@ -0,0 +1,202 @@
|
|||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/Assets/CustomResourceManager.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Common/VariantUtil.h"
|
||||
|
||||
#include "VideoCommon/AbstractGfx.h"
|
||||
#include "VideoCommon/Assets/CustomAsset.h"
|
||||
#include "VideoCommon/Assets/TextureAsset.h"
|
||||
#include "VideoCommon/BPMemory.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
#include "VideoCommon/VideoEvents.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
void CustomResourceManager::Initialize()
|
||||
{
|
||||
m_asset_loader.Initialize();
|
||||
|
||||
const size_t sys_mem = Common::MemPhysical();
|
||||
const size_t recommended_min_mem = 2 * size_t(1024 * 1024 * 1024);
|
||||
// keep 2GB memory for system stability if system RAM is 4GB+ - use half of memory in other cases
|
||||
m_max_ram_available =
|
||||
(sys_mem / 2 < recommended_min_mem) ? (sys_mem / 2) : (sys_mem - recommended_min_mem);
|
||||
|
||||
m_xfb_event = AfterFrameEvent::Register([this](Core::System&) { XFBTriggered(""); },
|
||||
"CustomResourceManager");
|
||||
}
|
||||
|
||||
void CustomResourceManager::Shutdown()
|
||||
{
|
||||
Reset();
|
||||
|
||||
m_asset_loader.Shutdown();
|
||||
}
|
||||
|
||||
void CustomResourceManager::Reset()
|
||||
{
|
||||
m_asset_loader.Reset(true);
|
||||
|
||||
m_loaded_assets = {};
|
||||
m_pending_assets = {};
|
||||
m_session_id_to_asset_data.clear();
|
||||
m_asset_id_to_session_id.clear();
|
||||
m_ram_used = 0;
|
||||
}
|
||||
|
||||
void CustomResourceManager::ReloadAsset(const CustomAssetLibrary::AssetID& asset_id)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_reload_mutex);
|
||||
m_assets_to_reload.insert(asset_id);
|
||||
}
|
||||
|
||||
CustomTextureData* CustomResourceManager::GetTextureDataFromAsset(
|
||||
const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
|
||||
{
|
||||
const auto [it, inserted] =
|
||||
m_texture_data_asset_cache.try_emplace(asset_id, InternalTextureDataResource{});
|
||||
if (it->second.asset_data &&
|
||||
it->second.asset_data->load_type == AssetData::LoadType::LoadFinalyzed)
|
||||
{
|
||||
m_loaded_assets.put(it->second.asset->GetSessionId(), it->second.asset);
|
||||
return &it->second.texture_data->m_texture;
|
||||
}
|
||||
|
||||
LoadTextureDataAsset(asset_id, std::move(library), &it->second);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CustomResourceManager::LoadTextureDataAsset(
|
||||
const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> library,
|
||||
InternalTextureDataResource* internal_texture_data)
|
||||
{
|
||||
if (!internal_texture_data->asset)
|
||||
{
|
||||
internal_texture_data->asset =
|
||||
CreateAsset<GameTextureAsset>(asset_id, AssetData::AssetType::TextureData, library);
|
||||
internal_texture_data->asset_data =
|
||||
&m_session_id_to_asset_data[internal_texture_data->asset->GetSessionId()];
|
||||
}
|
||||
|
||||
auto texture_data = internal_texture_data->asset->GetData();
|
||||
if (!texture_data ||
|
||||
internal_texture_data->asset_data->load_type == AssetData::LoadType::PendingReload)
|
||||
{
|
||||
// Tell the system we are still interested in loading this asset
|
||||
const auto session_id = internal_texture_data->asset->GetSessionId();
|
||||
m_pending_assets.put(session_id, m_session_id_to_asset_data[session_id].asset.get());
|
||||
}
|
||||
else if (internal_texture_data->asset_data->load_type == AssetData::LoadType::LoadFinished)
|
||||
{
|
||||
internal_texture_data->texture_data = std::move(texture_data);
|
||||
internal_texture_data->asset_data->load_type = AssetData::LoadType::LoadFinalyzed;
|
||||
}
|
||||
}
|
||||
|
||||
void CustomResourceManager::XFBTriggered(std::string_view)
|
||||
{
|
||||
std::set<std::size_t> session_ids_reloaded_this_frame;
|
||||
|
||||
// Look for any assets requested to be reloaded
|
||||
{
|
||||
decltype(m_assets_to_reload) assets_to_reload;
|
||||
|
||||
if (m_reload_mutex.try_lock())
|
||||
{
|
||||
std::swap(assets_to_reload, m_assets_to_reload);
|
||||
m_reload_mutex.unlock();
|
||||
}
|
||||
|
||||
for (const auto& asset_id : assets_to_reload)
|
||||
{
|
||||
if (const auto it = m_asset_id_to_session_id.find(asset_id);
|
||||
it != m_asset_id_to_session_id.end())
|
||||
{
|
||||
const auto session_id = it->second;
|
||||
session_ids_reloaded_this_frame.insert(session_id);
|
||||
AssetData& asset_data = m_session_id_to_asset_data[session_id];
|
||||
asset_data.load_type = AssetData::LoadType::PendingReload;
|
||||
asset_data.has_errors = false;
|
||||
for (const auto owner_session_id : asset_data.asset_owners)
|
||||
{
|
||||
AssetData& owner_asset_data = m_session_id_to_asset_data[owner_session_id];
|
||||
if (owner_asset_data.load_type == AssetData::LoadType::LoadFinalyzed)
|
||||
{
|
||||
owner_asset_data.load_type = AssetData::LoadType::DependenciesChanged;
|
||||
}
|
||||
}
|
||||
m_pending_assets.put(it->second, asset_data.asset.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ram_used > m_max_ram_available)
|
||||
{
|
||||
const u64 threshold_ram = 0.8f * m_max_ram_available;
|
||||
u64 ram_used = m_ram_used;
|
||||
|
||||
// Clear out least recently used resources until
|
||||
// we get safely in our threshold
|
||||
while (ram_used > threshold_ram && m_loaded_assets.size() > 0)
|
||||
{
|
||||
const auto asset = m_loaded_assets.pop();
|
||||
ram_used -= asset->GetByteSizeInMemory();
|
||||
|
||||
AssetData& asset_data = m_session_id_to_asset_data[asset->GetSessionId()];
|
||||
|
||||
if (asset_data.type == AssetData::AssetType::TextureData)
|
||||
{
|
||||
m_texture_data_asset_cache.erase(asset->GetAssetId());
|
||||
}
|
||||
asset_data.asset.reset();
|
||||
asset_data.load_type = AssetData::LoadType::Unloaded;
|
||||
}
|
||||
|
||||
// Recalculate to ensure accuracy
|
||||
m_ram_used = 0;
|
||||
for (const auto asset : m_loaded_assets.elements())
|
||||
{
|
||||
m_ram_used += asset->GetByteSizeInMemory();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pending_assets.empty())
|
||||
return;
|
||||
|
||||
const auto asset_session_ids_loaded =
|
||||
m_asset_loader.LoadAssets(m_pending_assets.elements(), m_ram_used, m_max_ram_available);
|
||||
for (const std::size_t session_id : asset_session_ids_loaded)
|
||||
{
|
||||
// While unlikely, if we loaded an asset in the previous frame but it was reloaded
|
||||
// this frame, we should ignore this load and wait on the reload
|
||||
if (session_ids_reloaded_this_frame.count(session_id) > 0) [[unlikely]]
|
||||
continue;
|
||||
|
||||
m_pending_assets.erase(session_id);
|
||||
|
||||
AssetData& asset_data = m_session_id_to_asset_data[session_id];
|
||||
m_loaded_assets.put(session_id, asset_data.asset.get());
|
||||
asset_data.load_type = AssetData::LoadType::LoadFinished;
|
||||
m_ram_used += asset_data.asset->GetByteSizeInMemory();
|
||||
|
||||
for (const auto owner_session_id : asset_data.asset_owners)
|
||||
{
|
||||
AssetData& owner_asset_data = m_session_id_to_asset_data[owner_session_id];
|
||||
if (owner_asset_data.load_type == AssetData::LoadType::LoadFinalyzed)
|
||||
{
|
||||
owner_asset_data.load_type = AssetData::LoadType::DependenciesChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
182
Source/Core/VideoCommon/Assets/CustomResourceManager.h
Normal file
182
Source/Core/VideoCommon/Assets/CustomResourceManager.h
Normal file
|
@ -0,0 +1,182 @@
|
|||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/HookableEvent.h"
|
||||
|
||||
#include "VideoCommon/Assets/CustomAsset.h"
|
||||
#include "VideoCommon/Assets/CustomAssetLibrary.h"
|
||||
#include "VideoCommon/Assets/CustomAssetLoader2.h"
|
||||
#include "VideoCommon/Assets/CustomTextureData.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
class GameTextureAsset;
|
||||
|
||||
class CustomResourceManager
|
||||
{
|
||||
public:
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
|
||||
void Reset();
|
||||
|
||||
// Requests that an asset that exists be reloaded
|
||||
void ReloadAsset(const CustomAssetLibrary::AssetID& asset_id);
|
||||
|
||||
void XFBTriggered(std::string_view texture_hash);
|
||||
|
||||
CustomTextureData*
|
||||
GetTextureDataFromAsset(const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
|
||||
|
||||
private:
|
||||
struct AssetData
|
||||
{
|
||||
std::unique_ptr<CustomAsset> asset;
|
||||
CustomAssetLibrary::TimeType load_request_time = {};
|
||||
std::set<std::size_t> asset_owners;
|
||||
|
||||
enum class AssetType
|
||||
{
|
||||
TextureData
|
||||
};
|
||||
AssetType type;
|
||||
|
||||
enum class LoadType
|
||||
{
|
||||
PendingReload,
|
||||
LoadFinished,
|
||||
LoadFinalyzed,
|
||||
DependenciesChanged,
|
||||
Unloaded
|
||||
};
|
||||
LoadType load_type = LoadType::PendingReload;
|
||||
bool has_errors = false;
|
||||
};
|
||||
|
||||
struct InternalTextureDataResource
|
||||
{
|
||||
AssetData* asset_data = nullptr;
|
||||
VideoCommon::GameTextureAsset* asset = nullptr;
|
||||
std::shared_ptr<TextureData> texture_data;
|
||||
};
|
||||
|
||||
void LoadTextureDataAsset(const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> library,
|
||||
InternalTextureDataResource* internal_texture_data);
|
||||
|
||||
template <typename T>
|
||||
T* CreateAsset(const CustomAssetLibrary::AssetID& asset_id, AssetData::AssetType asset_type,
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
|
||||
{
|
||||
const auto [it, added] =
|
||||
m_asset_id_to_session_id.try_emplace(asset_id, m_session_id_to_asset_data.size());
|
||||
|
||||
if (added)
|
||||
{
|
||||
AssetData asset_data;
|
||||
asset_data.asset = std::make_unique<T>(library, asset_id, it->second);
|
||||
asset_data.type = asset_type;
|
||||
asset_data.has_errors = false;
|
||||
asset_data.load_type = AssetData::LoadType::PendingReload;
|
||||
asset_data.load_request_time = {};
|
||||
|
||||
m_session_id_to_asset_data.insert_or_assign(it->second, std::move(asset_data));
|
||||
|
||||
// Synchronize the priority cache session id
|
||||
m_pending_assets.prepare();
|
||||
m_loaded_assets.prepare();
|
||||
}
|
||||
auto& asset_data_from_session = m_session_id_to_asset_data[it->second];
|
||||
|
||||
// Asset got unloaded, rebuild it with the same metadata
|
||||
if (!asset_data_from_session.asset)
|
||||
{
|
||||
asset_data_from_session.asset = std::make_unique<T>(library, asset_id, it->second);
|
||||
asset_data_from_session.has_errors = false;
|
||||
asset_data_from_session.load_type = AssetData::LoadType::PendingReload;
|
||||
}
|
||||
|
||||
return static_cast<T*>(asset_data_from_session.asset.get());
|
||||
}
|
||||
|
||||
class LeastRecentlyUsedCache
|
||||
{
|
||||
public:
|
||||
const std::list<CustomAsset*>& elements() const { return m_asset_cache; }
|
||||
|
||||
void put(u64 asset_session_id, CustomAsset* asset)
|
||||
{
|
||||
erase(asset_session_id);
|
||||
m_asset_cache.push_front(asset);
|
||||
m_iterator_lookup[m_asset_cache.front()->GetSessionId()] = m_asset_cache.begin();
|
||||
}
|
||||
|
||||
CustomAsset* pop()
|
||||
{
|
||||
if (m_asset_cache.empty()) [[unlikely]]
|
||||
return nullptr;
|
||||
const auto ret = m_asset_cache.back();
|
||||
if (ret != nullptr)
|
||||
{
|
||||
m_iterator_lookup[ret->GetSessionId()].reset();
|
||||
}
|
||||
m_asset_cache.pop_back();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void prepare() { m_iterator_lookup.push_back(std::nullopt); }
|
||||
|
||||
void erase(u64 asset_session_id)
|
||||
{
|
||||
if (const auto iter = m_iterator_lookup[asset_session_id])
|
||||
{
|
||||
m_asset_cache.erase(*iter);
|
||||
m_iterator_lookup[asset_session_id].reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool empty() const { return m_asset_cache.empty(); }
|
||||
|
||||
std::size_t size() const { return m_asset_cache.size(); }
|
||||
|
||||
private:
|
||||
std::list<CustomAsset*> m_asset_cache;
|
||||
|
||||
// Note: this vector is expected to be kept in sync with
|
||||
// the total amount of (unique) assets ever seen
|
||||
std::vector<std::optional<decltype(m_asset_cache)::iterator>> m_iterator_lookup;
|
||||
};
|
||||
|
||||
LeastRecentlyUsedCache m_loaded_assets;
|
||||
LeastRecentlyUsedCache m_pending_assets;
|
||||
|
||||
std::map<std::size_t, AssetData> m_session_id_to_asset_data;
|
||||
std::map<CustomAssetLibrary::AssetID, std::size_t> m_asset_id_to_session_id;
|
||||
|
||||
u64 m_ram_used = 0;
|
||||
u64 m_max_ram_available = 0;
|
||||
|
||||
std::map<CustomAssetLibrary::AssetID, InternalTextureDataResource> m_texture_data_asset_cache;
|
||||
|
||||
std::mutex m_reload_mutex;
|
||||
std::set<CustomAssetLibrary::AssetID> m_assets_to_reload;
|
||||
|
||||
CustomAssetLoader2 m_asset_loader;
|
||||
|
||||
Common::EventHook m_xfb_event;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
|
@ -14,6 +14,10 @@ add_library(videocommon
|
|||
Assets/CustomAssetLibrary.h
|
||||
Assets/CustomAssetLoader.cpp
|
||||
Assets/CustomAssetLoader.h
|
||||
Assets/CustomAssetLoader2.cpp
|
||||
Assets/CustomAssetLoader2.h
|
||||
Assets/CustomResourceManager.cpp
|
||||
Assets/CustomResourceManager.h
|
||||
Assets/CustomTextureData.cpp
|
||||
Assets/CustomTextureData.h
|
||||
Assets/DirectFilesystemAssetLibrary.cpp
|
||||
|
|
Loading…
Add table
Reference in a new issue