This commit is contained in:
iwubcode 2025-04-23 21:29:50 -04:00 committed by GitHub
commit 96a809a9bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 940 additions and 151 deletions

3
.gitmodules vendored
View file

@ -84,6 +84,9 @@
[submodule "Externals/Vulkan-Headers"]
path = Externals/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers.git
[submodule "Externals/watcher/watcher"]
path = Externals/watcher/watcher
url = https://github.com/e-dant/watcher.git
[submodule "Externals/SFML/SFML"]
path = Externals/SFML/SFML
url = https://github.com/SFML/SFML.git

View file

@ -784,6 +784,8 @@ if (USE_RETRO_ACHIEVEMENTS)
add_subdirectory(Externals/rcheevos)
endif()
add_subdirectory(Externals/watcher)
########################################
# Pre-build events: Define configuration variables and write SCM info header
#

4
Externals/watcher/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,4 @@
add_library(watcher INTERFACE IMPORTED GLOBAL)
set_target_properties(watcher PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/watcher/include
)

1
Externals/watcher/watcher vendored Submodule

@ -0,0 +1 @@
Subproject commit 0d6b9b409ccaed6313437ea3dc8b2fc078f3d25b

View file

@ -64,6 +64,8 @@ add_library(common
FatFsUtil.h
FileSearch.cpp
FileSearch.h
FilesystemWatcher.cpp
FilesystemWatcher.h
FileUtil.cpp
FileUtil.h
FixedSizeQueue.h
@ -183,6 +185,7 @@ PRIVATE
FatFs
Iconv::Iconv
spng::spng
watcher
${VTUNE_LIBRARIES}
)

View file

@ -0,0 +1,62 @@
// Copyright 2025 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/FilesystemWatcher.h"
#include <wtr/watcher.hpp>
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
namespace Common
{
FilesystemWatcher::FilesystemWatcher() = default;
FilesystemWatcher::~FilesystemWatcher() = default;
void FilesystemWatcher::Watch(const std::string& path)
{
const auto [iter, inserted] = m_watched_paths.try_emplace(path, nullptr);
if (inserted)
{
iter->second = std::make_unique<wtr::watch>(path, [this](wtr::event e) {
if (e.path_type == wtr::event::path_type::watcher)
{
return;
}
if (e.effect_type == wtr::event::effect_type::create)
{
const auto path = WithUnifiedPathSeparators(PathToString(e.path_name));
PathAdded(path);
}
else if (e.effect_type == wtr::event::effect_type::modify)
{
const auto path = WithUnifiedPathSeparators(PathToString(e.path_name));
PathModified(path);
}
else if (e.effect_type == wtr::event::effect_type::rename)
{
if (!e.associated)
{
WARN_LOG_FMT(COMMON, "Rename on path seen without association!");
return;
}
const auto old_path = WithUnifiedPathSeparators(PathToString(e.path_name));
const auto new_path = WithUnifiedPathSeparators(PathToString(e.associated->path_name));
PathRenamed(old_path, new_path);
}
else if (e.effect_type == wtr::event::effect_type::destroy)
{
const auto path = WithUnifiedPathSeparators(PathToString(e.path_name));
PathDeleted(path);
}
});
}
}
void FilesystemWatcher::Unwatch(const std::string& path)
{
m_watched_paths.erase(path);
}
} // namespace Common

View file

@ -0,0 +1,47 @@
// Copyright 2025 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <map>
#include <memory>
#include <string>
#include <string_view>
namespace wtr
{
inline namespace watcher
{
class watch;
}
} // namespace wtr
namespace Common
{
// A class that can watch a path and receive callbacks
// when files or directories underneath that path receive events
class FilesystemWatcher
{
public:
FilesystemWatcher();
virtual ~FilesystemWatcher();
void Watch(const std::string& path);
void Unwatch(const std::string& path);
private:
// A new file or folder was added to one of the watched paths
virtual void PathAdded(std::string_view path) {}
// A file or folder was modified in one of the watched paths
virtual void PathModified(std::string_view path) {}
// A file or folder was renamed in one of the watched paths
virtual void PathRenamed(std::string_view old_path, std::string_view new_path) {}
// A file or folder was deleted in one of the watched paths
virtual void PathDeleted(std::string_view path) {}
std::map<std::string, std::unique_ptr<wtr::watch>> m_watched_paths;
};
} // namespace Common

View file

@ -34,6 +34,7 @@
#include "IOS/USB/Emulated/Skylanders/Skylander.h"
#include "IOS/USB/USBScanner.h"
#include "VideoCommon/Assets/CustomAssetLoader.h"
#include "VideoCommon/Assets/CustomResourceManager.h"
#include "VideoCommon/CommandProcessor.h"
#include "VideoCommon/Fifo.h"
#include "VideoCommon/GeometryShaderManager.h"
@ -97,6 +98,7 @@ struct System::Impl
Interpreter m_interpreter;
JitInterface m_jit_interface;
VideoCommon::CustomAssetLoader m_custom_asset_loader;
VideoCommon::CustomResourceManager m_custom_resource_manager;
FifoPlayer m_fifo_player;
FifoRecorder m_fifo_recorder;
Movie::MovieManager m_movie;
@ -339,4 +341,9 @@ VideoCommon::CustomAssetLoader& System::GetCustomAssetLoader() const
{
return m_impl->m_custom_asset_loader;
}
VideoCommon::CustomResourceManager& System::GetCustomResourceManager() const
{
return m_impl->m_custom_resource_manager;
}
} // namespace Core

View file

@ -109,7 +109,8 @@ class SystemTimersManager;
namespace VideoCommon
{
class CustomAssetLoader;
}
class CustomResourceManager;
} // namespace VideoCommon
namespace VideoInterface
{
class VideoInterfaceManager;
@ -198,6 +199,7 @@ public:
XFStateManager& GetXFStateManager() const;
VideoInterface::VideoInterfaceManager& GetVideoInterface() const;
VideoCommon::CustomAssetLoader& GetCustomAssetLoader() const;
VideoCommon::CustomResourceManager& GetCustomResourceManager() const;
private:
System();

View file

@ -56,6 +56,7 @@
<ClInclude Include="Common\Event.h" />
<ClInclude Include="Common\FatFsUtil.h" />
<ClInclude Include="Common\FileSearch.h" />
<ClInclude Include="Common\FilesystemWatcher.h" />
<ClInclude Include="Common\FileUtil.h" />
<ClInclude Include="Common\FixedSizeQueue.h" />
<ClInclude Include="Common\Flag.h" />
@ -663,12 +664,16 @@
<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" />
<ClInclude Include="VideoCommon\Assets\MeshAsset.h" />
<ClInclude Include="VideoCommon\Assets\ShaderAsset.h" />
<ClInclude Include="VideoCommon\Assets\TextureAsset.h" />
<ClInclude Include="VideoCommon\Assets\Types.h" />
<ClInclude Include="VideoCommon\Assets\WatchableFilesystemAssetLibrary.h" />
<ClInclude Include="VideoCommon\AsyncRequests.h" />
<ClInclude Include="VideoCommon\AsyncShaderCompiler.h" />
<ClInclude Include="VideoCommon\BoundingBox.h" />
@ -805,6 +810,7 @@
<ClCompile Include="Common\ENet.cpp" />
<ClCompile Include="Common\FatFsUtil.cpp" />
<ClCompile Include="Common\FileSearch.cpp" />
<ClCompile Include="Common\FilesystemWatcher.cpp" />
<ClCompile Include="Common\FileUtil.cpp" />
<ClCompile Include="Common\FloatUtils.cpp" />
<ClCompile Include="Common\GekkoDisassembler.cpp" />
@ -1311,6 +1317,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" />

View file

@ -6,8 +6,8 @@
namespace VideoCommon
{
CustomAsset::CustomAsset(std::shared_ptr<CustomAssetLibrary> library,
const CustomAssetLibrary::AssetID& asset_id)
: m_owning_library(std::move(library)), m_asset_id(asset_id)
const CustomAssetLibrary::AssetID& asset_id, u64 session_id)
: m_owning_library(std::move(library)), m_asset_id(asset_id), m_session_id(session_id)
{
}
@ -34,6 +34,11 @@ const CustomAssetLibrary::TimeType& CustomAsset::GetLastLoadedTime() const
return m_last_loaded_time;
}
std::size_t CustomAsset::GetSessionId() const
{
return m_session_id;
}
const CustomAssetLibrary::AssetID& CustomAsset::GetAssetId() const
{
return m_asset_id;

View file

@ -18,7 +18,7 @@ class CustomAsset
{
public:
CustomAsset(std::shared_ptr<CustomAssetLibrary> library,
const CustomAssetLibrary::AssetID& asset_id);
const CustomAssetLibrary::AssetID& asset_id, u64 session_id);
virtual ~CustomAsset() = default;
CustomAsset(const CustomAsset&) = delete;
CustomAsset(CustomAsset&&) = delete;
@ -39,6 +39,9 @@ public:
// Returns an id that uniquely identifies this asset
const CustomAssetLibrary::AssetID& GetAssetId() const;
// Returns an id that is unique to this session
std::size_t GetSessionId() const;
// A rough estimate of how much space this asset
// will take in memroy
std::size_t GetByteSizeInMemory() const;
@ -49,6 +52,7 @@ protected:
private:
virtual CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) = 0;
CustomAssetLibrary::AssetID m_asset_id;
std::size_t m_session_id;
mutable std::mutex m_info_lock;
std::size_t m_bytes_loaded = 0;

View file

@ -67,19 +67,20 @@ private:
if (shared)
return shared;
}
std::shared_ptr<AssetType> ptr(new AssetType(std::move(library), asset_id), [&](AssetType* a) {
{
std::lock_guard lk(m_asset_load_lock);
m_total_bytes_loaded -= a->GetByteSizeInMemory();
m_assets_to_monitor.erase(a->GetAssetId());
if (m_max_memory_available >= m_total_bytes_loaded && m_memory_exceeded)
{
INFO_LOG_FMT(VIDEO, "Asset memory went below limit, new assets can begin loading.");
m_memory_exceeded = false;
}
}
delete a;
});
std::shared_ptr<AssetType> ptr(
new AssetType(std::move(library), asset_id, asset_map.size()), [&](AssetType* a) {
{
std::lock_guard lk(m_asset_load_lock);
m_total_bytes_loaded -= a->GetByteSizeInMemory();
m_assets_to_monitor.erase(a->GetAssetId());
if (m_max_memory_available >= m_total_bytes_loaded && m_memory_exceeded)
{
INFO_LOG_FMT(VIDEO, "Asset memory went below limit, new assets can begin loading.");
m_memory_exceeded = false;
}
}
delete a;
});
it->second = ptr;
m_asset_load_thread.Push(it->second);
return ptr;

View 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

View 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

View 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

View 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

View file

@ -13,6 +13,8 @@
#include "Common/JsonUtil.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Core/System.h"
#include "VideoCommon/Assets/CustomResourceManager.h"
#include "VideoCommon/Assets/MaterialAsset.h"
#include "VideoCommon/Assets/MeshAsset.h"
#include "VideoCommon/Assets/ShaderAsset.h"
@ -53,7 +55,7 @@ std::size_t GetAssetSize(const CustomTextureData& data)
CustomAssetLibrary::TimeType
DirectFilesystemAssetLibrary::GetLastAssetWriteTime(const AssetID& asset_id) const
{
std::lock_guard lk(m_lock);
std::lock_guard lk(m_asset_map_lock);
if (auto iter = m_assetid_to_asset_map_path.find(asset_id);
iter != m_assetid_to_asset_map_path.end())
{
@ -434,10 +436,42 @@ CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadTexture(const Ass
}
void DirectFilesystemAssetLibrary::SetAssetIDMapData(const AssetID& asset_id,
AssetMap asset_path_map)
VideoCommon::Assets::AssetMap asset_path_map)
{
std::lock_guard lk(m_lock);
m_assetid_to_asset_map_path[asset_id] = std::move(asset_path_map);
VideoCommon::Assets::AssetMap previous_asset_map;
{
std::lock_guard lk(m_asset_map_lock);
previous_asset_map = m_assetid_to_asset_map_path[asset_id];
}
{
std::lock_guard lk(m_path_map_lock);
for (const auto& [name, path] : previous_asset_map)
{
m_path_to_asset_id.erase(PathToString(path));
}
for (const auto& [name, path] : asset_path_map)
{
m_path_to_asset_id[PathToString(path)] = asset_id;
}
}
{
std::lock_guard lk(m_asset_map_lock);
m_assetid_to_asset_map_path[asset_id] = std::move(asset_path_map);
}
}
void DirectFilesystemAssetLibrary::PathModified(std::string_view path)
{
std::lock_guard lk(m_path_map_lock);
if (const auto iter = m_path_to_asset_id.find(path); iter != m_path_to_asset_id.end())
{
auto& system = Core::System::GetInstance();
auto& resource_manager = system.GetCustomResourceManager();
resource_manager.ReloadAsset(iter->second);
}
}
bool DirectFilesystemAssetLibrary::LoadMips(const std::filesystem::path& asset_path,
@ -492,10 +526,10 @@ bool DirectFilesystemAssetLibrary::LoadMips(const std::filesystem::path& asset_p
return true;
}
DirectFilesystemAssetLibrary::AssetMap
VideoCommon::Assets::AssetMap
DirectFilesystemAssetLibrary::GetAssetMapForID(const AssetID& asset_id) const
{
std::lock_guard lk(m_lock);
std::lock_guard lk(m_asset_map_lock);
if (auto iter = m_assetid_to_asset_map_path.find(asset_id);
iter != m_assetid_to_asset_map_path.end())
{

View file

@ -8,18 +8,17 @@
#include <mutex>
#include <string>
#include "VideoCommon/Assets/CustomAssetLibrary.h"
#include "VideoCommon/Assets/CustomTextureData.h"
#include "VideoCommon/Assets/Types.h"
#include "VideoCommon/Assets/WatchableFilesystemAssetLibrary.h"
namespace VideoCommon
{
// This class implements 'CustomAssetLibrary' and loads any assets
// directly from the filesystem
class DirectFilesystemAssetLibrary final : public CustomAssetLibrary
class DirectFilesystemAssetLibrary final : public WatchableFilesystemAssetLibrary
{
public:
using AssetMap = std::map<std::string, std::filesystem::path>;
LoadInfo LoadTexture(const AssetID& asset_id, TextureData* data) override;
LoadInfo LoadPixelShader(const AssetID& asset_id, PixelShaderData* data) override;
LoadInfo LoadMaterial(const AssetID& asset_id, MaterialData* data) override;
@ -31,16 +30,21 @@ public:
// Assigns the asset id to a map of files, how this map is read is dependent on the data
// For instance, a raw texture would expect the map to have a single entry and load that
// file as the asset. But a model file data might have its data spread across multiple files
void SetAssetIDMapData(const AssetID& asset_id, AssetMap asset_path_map);
void SetAssetIDMapData(const AssetID& asset_id, Assets::AssetMap asset_path_map);
private:
void PathModified(std::string_view path) override;
// Loads additional mip levels into the texture structure until _mip<N> texture is not found
bool LoadMips(const std::filesystem::path& asset_path, CustomTextureData::ArraySlice* data);
// Gets the asset map given an asset id
AssetMap GetAssetMapForID(const AssetID& asset_id) const;
Assets::AssetMap GetAssetMapForID(const AssetID& asset_id) const;
mutable std::mutex m_lock;
std::map<AssetID, std::map<std::string, std::filesystem::path>> m_assetid_to_asset_map_path;
mutable std::mutex m_asset_map_lock;
std::map<AssetID, Assets::AssetMap> m_assetid_to_asset_map_path;
mutable std::mutex m_path_map_lock;
std::map<std::string, AssetID, std::less<>> m_path_to_asset_id;
};
} // namespace VideoCommon

View file

@ -0,0 +1,13 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <filesystem>
#include <map>
#include <string>
namespace VideoCommon::Assets
{
using AssetMap = std::map<std::string, std::filesystem::path>;
}

View file

@ -0,0 +1,14 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Common/FilesystemWatcher.h"
#include "VideoCommon/Assets/CustomAssetLibrary.h"
namespace VideoCommon
{
class WatchableFilesystemAssetLibrary : public CustomAssetLibrary, public Common::FilesystemWatcher
{
};
} // namespace VideoCommon

View file

@ -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
@ -26,6 +30,8 @@ add_library(videocommon
Assets/ShaderAsset.h
Assets/TextureAsset.cpp
Assets/TextureAsset.h
Assets/Types.h
Assets/WatchableFilesystemAssetLibrary.h
AsyncRequests.cpp
AsyncRequests.h
AsyncShaderCompiler.cpp

View file

@ -7,12 +7,13 @@
#include <picojson.h>
#include "VideoCommon/Assets/DirectFilesystemAssetLibrary.h"
#include "VideoCommon/Assets/CustomAssetLibrary.h"
#include "VideoCommon/Assets/Types.h"
struct GraphicsModAssetConfig
{
VideoCommon::CustomAssetLibrary::AssetID m_asset_id;
VideoCommon::DirectFilesystemAssetLibrary::AssetMap m_map;
VideoCommon::Assets::AssetMap m_map;
void SerializeToConfig(picojson::object& json_obj) const;
bool DeserializeFromConfig(const picojson::object& obj);

View file

@ -25,7 +25,7 @@
#include "Core/ConfigManager.h"
#include "Core/System.h"
#include "VideoCommon/Assets/CustomAsset.h"
#include "VideoCommon/Assets/CustomAssetLoader.h"
#include "VideoCommon/Assets/CustomResourceManager.h"
#include "VideoCommon/Assets/DirectFilesystemAssetLibrary.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/VideoConfig.h"
@ -95,10 +95,11 @@ void HiresTexture::Update()
GetTextureDirectoriesWithGameId(File::GetUserPath(D_HIRESTEXTURES_IDX), game_id);
const std::vector<std::string> extensions{".png", ".dds"};
auto& system = Core::System::GetInstance();
for (const auto& texture_directory : texture_directories)
{
// Watch this directory for any texture reloads
s_file_library->Watch(texture_directory);
const auto texture_paths =
Common::DoFileSearch({texture_directory}, extensions, /*recursive*/ true);
@ -130,10 +131,10 @@ void HiresTexture::Update()
if (g_ActiveConfig.bCacheHiresTextures)
{
auto hires_texture = std::make_shared<HiresTexture>(
has_arbitrary_mipmaps,
system.GetCustomAssetLoader().LoadGameTexture(filename, s_file_library));
s_hires_texture_cache.try_emplace(filename, std::move(hires_texture));
auto hires_texture =
std::make_shared<HiresTexture>(has_arbitrary_mipmaps, std::move(filename));
static_cast<void>(hires_texture->LoadTexture());
s_hires_texture_cache.try_emplace(hires_texture->GetId(), hires_texture);
}
}
}
@ -167,7 +168,7 @@ void HiresTexture::Clear()
std::shared_ptr<HiresTexture> HiresTexture::Search(const TextureInfo& texture_info)
{
const auto [base_filename, has_arb_mipmaps] = GetNameArbPair(texture_info);
auto [base_filename, has_arb_mipmaps] = GetNameArbPair(texture_info);
if (base_filename == "")
return nullptr;
@ -177,24 +178,27 @@ std::shared_ptr<HiresTexture> HiresTexture::Search(const TextureInfo& texture_in
}
else
{
auto& system = Core::System::GetInstance();
auto hires_texture = std::make_shared<HiresTexture>(
has_arb_mipmaps,
system.GetCustomAssetLoader().LoadGameTexture(base_filename, s_file_library));
auto hires_texture = std::make_shared<HiresTexture>(has_arb_mipmaps, std::move(base_filename));
if (g_ActiveConfig.bCacheHiresTextures)
{
s_hires_texture_cache.try_emplace(base_filename, hires_texture);
s_hires_texture_cache.try_emplace(hires_texture->GetId(), hires_texture);
}
return hires_texture;
}
}
HiresTexture::HiresTexture(bool has_arbitrary_mipmaps,
std::shared_ptr<VideoCommon::GameTextureAsset> asset)
: m_has_arbitrary_mipmaps(has_arbitrary_mipmaps), m_game_texture(std::move(asset))
HiresTexture::HiresTexture(bool has_arbitrary_mipmaps, std::string id)
: m_has_arbitrary_mipmaps(has_arbitrary_mipmaps), m_id(std::move(id))
{
}
VideoCommon::CustomTextureData* HiresTexture::LoadTexture() const
{
auto& system = Core::System::GetInstance();
auto& custom_resource_manager = system.GetCustomResourceManager();
return custom_resource_manager.GetTextureDataFromAsset(m_id, s_file_library);
}
std::set<std::string> GetTextureDirectoriesWithGameId(const std::string& root_directory,
const std::string& game_id)
{

View file

@ -10,7 +10,6 @@
#include "Common/CommonTypes.h"
#include "VideoCommon/Assets/CustomTextureData.h"
#include "VideoCommon/Assets/TextureAsset.h"
#include "VideoCommon/TextureConfig.h"
#include "VideoCommon/TextureInfo.h"
@ -27,12 +26,13 @@ public:
static void Shutdown();
static std::shared_ptr<HiresTexture> Search(const TextureInfo& texture_info);
HiresTexture(bool has_arbitrary_mipmaps, std::shared_ptr<VideoCommon::GameTextureAsset> asset);
HiresTexture(bool has_arbitrary_mipmaps, std::string id);
bool HasArbitraryMipmaps() const { return m_has_arbitrary_mipmaps; }
const std::shared_ptr<VideoCommon::GameTextureAsset>& GetAsset() const { return m_game_texture; }
VideoCommon::CustomTextureData* LoadTexture() const;
const std::string& GetId() const { return m_id; }
private:
bool m_has_arbitrary_mipmaps = false;
std::shared_ptr<VideoCommon::GameTextureAsset> m_game_texture;
std::string m_id;
};

View file

@ -37,13 +37,13 @@
#include "VideoCommon/AbstractFramebuffer.h"
#include "VideoCommon/AbstractGfx.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/Assets/CustomResourceManager.h"
#include "VideoCommon/Assets/CustomTextureData.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/GraphicsModSystem/Runtime/FBInfo.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h"
#include "VideoCommon/HiresTextures.h"
#include "VideoCommon/OpcodeDecoding.h"
#include "VideoCommon/PixelShaderManager.h"
#include "VideoCommon/Present.h"
@ -262,25 +262,10 @@ void TextureCacheBase::SetBackupConfig(const VideoConfig& config)
bool TextureCacheBase::DidLinkedAssetsChange(const TCacheEntry& entry)
{
for (const auto& cached_asset : entry.linked_game_texture_assets)
{
if (cached_asset.m_asset)
{
if (cached_asset.m_asset->GetLastLoadedTime() > cached_asset.m_cached_write_time)
return true;
}
}
if (!entry.hires_texture)
return false;
for (const auto& cached_asset : entry.linked_asset_dependencies)
{
if (cached_asset.m_asset)
{
if (cached_asset.m_asset->GetLastLoadedTime() > cached_asset.m_cached_write_time)
return true;
}
}
return false;
return entry.last_custom_texture_data != entry.hires_texture->LoadTexture();
}
RcTcacheEntry TextureCacheBase::ApplyPaletteToEntry(RcTcacheEntry& entry, const u8* palette,
@ -1566,80 +1551,42 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
InvalidateTexture(oldest_entry);
}
std::vector<VideoCommon::CachedAsset<VideoCommon::GameTextureAsset>> cached_game_assets;
std::vector<std::shared_ptr<VideoCommon::TextureData>> data_for_assets;
std::shared_ptr<HiresTexture> hires_texture;
bool has_arbitrary_mipmaps = false;
bool skip_texture_dump = false;
std::shared_ptr<HiresTexture> hires_texture;
VideoCommon::CustomTextureData* custom_texture_data = nullptr;
if (g_ActiveConfig.bHiresTextures)
{
hires_texture = HiresTexture::Search(texture_info);
if (hires_texture)
{
auto asset = hires_texture->GetAsset();
const auto loaded_time = asset->GetLastLoadedTime();
cached_game_assets.push_back(
VideoCommon::CachedAsset<VideoCommon::GameTextureAsset>{std::move(asset), loaded_time});
has_arbitrary_mipmaps = hires_texture->HasArbitraryMipmaps();
custom_texture_data = hires_texture->LoadTexture();
skip_texture_dump = true;
}
}
std::vector<VideoCommon::CachedAsset<VideoCommon::CustomAsset>> additional_dependencies;
std::string texture_name = "";
if (g_ActiveConfig.bGraphicMods)
{
u32 height = texture_info.GetRawHeight();
u32 width = texture_info.GetRawWidth();
if (hires_texture)
{
auto asset = hires_texture->GetAsset();
if (asset)
{
auto data = asset->GetData();
if (data)
{
if (!data->m_texture.m_slices.empty())
{
if (!data->m_texture.m_slices[0].m_levels.empty())
{
height = data->m_texture.m_slices[0].m_levels[0].height;
width = data->m_texture.m_slices[0].m_levels[0].width;
}
}
}
}
}
texture_name = texture_info.CalculateTextureName().GetFullName();
GraphicsModActionData::TextureCreate texture_create{
texture_name, width, height, &cached_game_assets, &additional_dependencies};
GraphicsModActionData::TextureCreate texture_create{texture_name, width, height, nullptr,
nullptr};
for (const auto& action : g_graphics_mod_manager->GetTextureCreateActions(texture_name))
{
action->OnTextureCreate(&texture_create);
}
}
data_for_assets.reserve(cached_game_assets.size());
for (auto& cached_asset : cached_game_assets)
{
auto data = cached_asset.m_asset->GetData();
if (data)
{
if (cached_asset.m_asset->Validate(texture_info.GetRawWidth(), texture_info.GetRawHeight()))
{
data_for_assets.push_back(data);
}
}
}
auto entry =
CreateTextureEntry(TextureCreationInfo{base_hash, full_hash, bytes_per_block, palette_size},
texture_info, textureCacheSafetyColorSampleSize,
std::move(data_for_assets), has_arbitrary_mipmaps, skip_texture_dump);
entry->linked_game_texture_assets = std::move(cached_game_assets);
entry->linked_asset_dependencies = std::move(additional_dependencies);
texture_info, textureCacheSafetyColorSampleSize, custom_texture_data,
has_arbitrary_mipmaps, skip_texture_dump);
entry->hires_texture = std::move(hires_texture);
entry->last_custom_texture_data = custom_texture_data;
entry->texture_info_name = std::move(texture_name);
return entry;
}
@ -1649,8 +1596,7 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
// expected because each texture is loaded into a texture array
RcTcacheEntry TextureCacheBase::CreateTextureEntry(
const TextureCreationInfo& creation_info, const TextureInfo& texture_info,
const int safety_color_sample_size,
std::vector<std::shared_ptr<VideoCommon::TextureData>> assets_data,
const int safety_color_sample_size, VideoCommon::CustomTextureData* custom_texture_data,
const bool custom_arbitrary_mipmaps, bool skip_texture_dump)
{
#ifdef __APPLE__
@ -1660,33 +1606,22 @@ RcTcacheEntry TextureCacheBase::CreateTextureEntry(
#endif
RcTcacheEntry entry;
if (!assets_data.empty())
if (custom_texture_data)
{
const auto calculate_max_levels = [&]() {
const auto max_element = std::ranges::max_element(
assets_data, {}, [](const auto& v) { return v->m_texture.m_slices[0].m_levels.size(); });
return (*max_element)->m_texture.m_slices[0].m_levels.size();
};
const u32 texLevels = no_mips ? 1 : (u32)calculate_max_levels();
const auto& first_level = assets_data[0]->m_texture.m_slices[0].m_levels[0];
const TextureConfig config(first_level.width, first_level.height, texLevels,
static_cast<u32>(assets_data.size()), 1, first_level.format, 0,
AbstractTextureType::Texture_2DArray);
const u32 texLevels = no_mips ? 1 : (u32)custom_texture_data->m_slices[0].m_levels.size();
const auto& first_level = custom_texture_data->m_slices[0].m_levels[0];
const TextureConfig config(first_level.width, first_level.height, texLevels, 1, 1,
first_level.format, 0, AbstractTextureType::Texture_2DArray);
entry = AllocateCacheEntry(config);
if (!entry) [[unlikely]]
return entry;
for (u32 data_index = 0; data_index < static_cast<u32>(assets_data.size()); data_index++)
const auto& slice = custom_texture_data->m_slices[0];
for (u32 level_index = 0;
level_index < std::min(texLevels, static_cast<u32>(slice.m_levels.size())); ++level_index)
{
const auto& asset = assets_data[data_index];
const auto& slice = asset->m_texture.m_slices[0];
for (u32 level_index = 0;
level_index < std::min(texLevels, static_cast<u32>(slice.m_levels.size()));
++level_index)
{
const auto& level = slice.m_levels[level_index];
entry->texture->Load(level_index, level.width, level.height, level.row_length,
level.data.data(), level.data.size(), data_index);
}
const auto& level = slice.m_levels[level_index];
entry->texture->Load(level_index, level.width, level.height, level.row_length,
level.data.data(), level.data.size());
}
entry->has_arbitrary_mips = custom_arbitrary_mipmaps;

View file

@ -24,6 +24,7 @@
#include "VideoCommon/AbstractTexture.h"
#include "VideoCommon/Assets/CustomAsset.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/HiresTextures.h"
#include "VideoCommon/TextureConfig.h"
#include "VideoCommon/TextureDecoder.h"
#include "VideoCommon/TextureInfo.h"
@ -167,8 +168,8 @@ struct TCacheEntry
std::string texture_info_name = "";
std::vector<VideoCommon::CachedAsset<VideoCommon::GameTextureAsset>> linked_game_texture_assets;
std::vector<VideoCommon::CachedAsset<VideoCommon::CustomAsset>> linked_asset_dependencies;
VideoCommon::CustomTextureData* last_custom_texture_data;
std::shared_ptr<HiresTexture> hires_texture;
explicit TCacheEntry(std::unique_ptr<AbstractTexture> tex,
std::unique_ptr<AbstractFramebuffer> fb);
@ -351,11 +352,10 @@ private:
void SetBackupConfig(const VideoConfig& config);
RcTcacheEntry
CreateTextureEntry(const TextureCreationInfo& creation_info, const TextureInfo& texture_info,
int safety_color_sample_size,
std::vector<std::shared_ptr<VideoCommon::TextureData>> assets_data,
bool custom_arbitrary_mipmaps, bool skip_texture_dump);
RcTcacheEntry CreateTextureEntry(const TextureCreationInfo& creation_info,
const TextureInfo& texture_info, int safety_color_sample_size,
VideoCommon::CustomTextureData* custom_texture_data,
bool custom_arbitrary_mipmaps, bool skip_texture_dump);
RcTcacheEntry GetXFBFromCache(u32 address, u32 width, u32 height, u32 stride);

View file

@ -41,6 +41,7 @@
#endif
#include "VideoCommon/AbstractGfx.h"
#include "VideoCommon/Assets/CustomResourceManager.h"
#include "VideoCommon/AsyncRequests.h"
#include "VideoCommon/BPStructs.h"
#include "VideoCommon/BoundingBox.h"
@ -341,12 +342,16 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr<AbstractGfx> gfx,
}
g_shader_cache->InitializeShaderCache();
system.GetCustomResourceManager().Initialize();
return true;
}
void VideoBackendBase::ShutdownShared()
{
auto& system = Core::System::GetInstance();
system.GetCustomResourceManager().Shutdown();
g_frame_dumper.reset();
g_presenter.reset();
@ -369,7 +374,6 @@ void VideoBackendBase::ShutdownShared()
m_initialized = false;
auto& system = Core::System::GetInstance();
VertexLoaderManager::Clear();
system.GetFifo().Shutdown();
}

View file

@ -16,6 +16,7 @@
<AdditionalIncludeDirectories>$(ExternalsDir)rangeset\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ExternalsDir)Vulkan-Headers\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ExternalsDir)VulkanMemoryAllocator\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ExternalsDir)watcher\watcher\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ExternalsDir)WIL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<!--WIL doesn't have it's own vcxproj/exports, and no externals reference WIL, so this is fine to define only for Dolphin-->
<PreprocessorDefinitions>WIL_SUPPRESS_EXCEPTIONS;%(PreprocessorDefinitions)</PreprocessorDefinitions>