summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorAdmiral H. Curtiss <pikachu025@gmail.com>2023-06-03 01:11:41 +0200
committerGitHub <noreply@github.com>2023-06-03 01:11:41 +0200
commitd03e09c8fd911d1b4e66184ceedbeca989b75a0c (patch)
treec55f05ebbe08f611ab6eb44952ff389c3bdbdd9f /Source/Core
parent4c9210b439c72e726d41229c73eff881412beb48 (diff)
parentc93940c6ee8194fa48663b9bcd0f153c9d72a3ea (diff)
Merge pull request #11877 from iwubcode/asset_library_loader
VideoCommon: add multithreaded asset loader and define a texture asset
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/DolphinLib.props4
-rw-r--r--Source/Core/VideoCommon/Assets/CustomAssetLoader.cpp93
-rw-r--r--Source/Core/VideoCommon/Assets/CustomAssetLoader.h81
-rw-r--r--Source/Core/VideoCommon/Assets/TextureAsset.cpp77
-rw-r--r--Source/Core/VideoCommon/Assets/TextureAsset.h32
-rw-r--r--Source/Core/VideoCommon/CMakeLists.txt4
6 files changed, 291 insertions, 0 deletions
diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props
index 5ae7b6fd67..05142ae672 100644
--- a/Source/Core/DolphinLib.props
+++ b/Source/Core/DolphinLib.props
@@ -633,8 +633,10 @@
<ClInclude Include="VideoCommon\AbstractTexture.h" />
<ClInclude Include="VideoCommon\Assets\CustomAsset.h" />
<ClInclude Include="VideoCommon\Assets\CustomAssetLibrary.h" />
+ <ClInclude Include="VideoCommon\Assets\CustomAssetLoader.h" />
<ClInclude Include="VideoCommon\Assets\CustomTextureData.h" />
<ClInclude Include="VideoCommon\Assets\DirectFilesystemAssetLibrary.h" />
+ <ClInclude Include="VideoCommon\Assets\TextureAsset.h" />
<ClInclude Include="VideoCommon\AsyncRequests.h" />
<ClInclude Include="VideoCommon\AsyncShaderCompiler.h" />
<ClInclude Include="VideoCommon\BoundingBox.h" />
@@ -1246,8 +1248,10 @@
<ClCompile Include="VideoCommon\AbstractTexture.cpp" />
<ClCompile Include="VideoCommon\Assets\CustomAsset.cpp" />
<ClCompile Include="VideoCommon\Assets\CustomAssetLibrary.cpp" />
+ <ClCompile Include="VideoCommon\Assets\CustomAssetLoader.cpp" />
<ClCompile Include="VideoCommon\Assets\CustomTextureData.cpp" />
<ClCompile Include="VideoCommon\Assets\DirectFilesystemAssetLibrary.cpp" />
+ <ClCompile Include="VideoCommon\Assets\TextureAsset.cpp" />
<ClCompile Include="VideoCommon\AsyncRequests.cpp" />
<ClCompile Include="VideoCommon\AsyncShaderCompiler.cpp" />
<ClCompile Include="VideoCommon\BoundingBox.cpp" />
diff --git a/Source/Core/VideoCommon/Assets/CustomAssetLoader.cpp b/Source/Core/VideoCommon/Assets/CustomAssetLoader.cpp
new file mode 100644
index 0000000000..d2189e655b
--- /dev/null
+++ b/Source/Core/VideoCommon/Assets/CustomAssetLoader.cpp
@@ -0,0 +1,93 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "VideoCommon/Assets/CustomAssetLoader.h"
+
+#include "Common/Logging/Log.h"
+#include "Common/MemoryUtil.h"
+#include "VideoCommon/Assets/CustomAssetLibrary.h"
+
+namespace VideoCommon
+{
+void CustomAssetLoader::Init()
+{
+ m_asset_monitor_thread_shutdown.Clear();
+
+ 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_memory_available =
+ (sys_mem / 2 < recommended_min_mem) ? (sys_mem / 2) : (sys_mem - recommended_min_mem);
+
+ m_asset_monitor_thread = std::thread([this]() {
+ Common::SetCurrentThreadName("Asset monitor");
+ while (true)
+ {
+ if (m_asset_monitor_thread_shutdown.IsSet())
+ {
+ break;
+ }
+
+ std::this_thread::sleep_for(TIME_BETWEEN_ASSET_MONITOR_CHECKS);
+
+ std::lock_guard lk(m_assets_lock);
+ for (auto& [asset_id, asset_to_monitor] : m_assets_to_monitor)
+ {
+ if (auto ptr = asset_to_monitor.lock())
+ {
+ const auto write_time = ptr->GetLastWriteTime();
+ if (write_time > ptr->GetLastLoadedTime())
+ {
+ (void)ptr->Load();
+ }
+ }
+ }
+ }
+ });
+
+ m_asset_load_thread.Reset("Custom Asset Loader", [this](std::weak_ptr<CustomAsset> asset) {
+ if (auto ptr = asset.lock())
+ {
+ if (ptr->Load())
+ {
+ if (m_max_memory_available >= m_total_bytes_loaded + ptr->GetByteSizeInMemory())
+ {
+ m_total_bytes_loaded += ptr->GetByteSizeInMemory();
+
+ std::lock_guard lk(m_assets_lock);
+ m_assets_to_monitor.try_emplace(ptr->GetAssetId(), ptr);
+ }
+ else
+ {
+ ERROR_LOG_FMT(VIDEO, "Failed to load asset {} because there was not enough memory.",
+ ptr->GetAssetId());
+ }
+ }
+ }
+ });
+}
+
+void CustomAssetLoader ::Shutdown()
+{
+ m_asset_load_thread.Shutdown(true);
+
+ m_asset_monitor_thread_shutdown.Set();
+ m_asset_monitor_thread.join();
+ m_assets_to_monitor.clear();
+ m_total_bytes_loaded = 0;
+}
+
+std::shared_ptr<RawTextureAsset>
+CustomAssetLoader::LoadTexture(const CustomAssetLibrary::AssetID& asset_id,
+ std::shared_ptr<CustomAssetLibrary> library)
+{
+ return LoadOrCreateAsset<RawTextureAsset>(asset_id, m_textures, std::move(library));
+}
+
+std::shared_ptr<GameTextureAsset>
+CustomAssetLoader::LoadGameTexture(const CustomAssetLibrary::AssetID& asset_id,
+ std::shared_ptr<CustomAssetLibrary> library)
+{
+ return LoadOrCreateAsset<GameTextureAsset>(asset_id, m_game_textures, std::move(library));
+}
+} // namespace VideoCommon
diff --git a/Source/Core/VideoCommon/Assets/CustomAssetLoader.h b/Source/Core/VideoCommon/Assets/CustomAssetLoader.h
new file mode 100644
index 0000000000..da1f54b133
--- /dev/null
+++ b/Source/Core/VideoCommon/Assets/CustomAssetLoader.h
@@ -0,0 +1,81 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <chrono>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include "Common/Flag.h"
+#include "Common/WorkQueueThread.h"
+#include "VideoCommon/Assets/CustomAsset.h"
+#include "VideoCommon/Assets/TextureAsset.h"
+
+namespace VideoCommon
+{
+// This class is responsible for loading data asynchronously when requested
+// and watches that data asynchronously reloading it if it changes
+class CustomAssetLoader
+{
+public:
+ CustomAssetLoader() = default;
+ ~CustomAssetLoader() = default;
+ CustomAssetLoader(const CustomAssetLoader&) = delete;
+ CustomAssetLoader(CustomAssetLoader&&) = delete;
+ CustomAssetLoader& operator=(const CustomAssetLoader&) = delete;
+ CustomAssetLoader& operator=(CustomAssetLoader&&) = delete;
+
+ void Init();
+ void Shutdown();
+
+ // The following Load* functions will load or create an asset associated
+ // with the given asset id
+ // Loads happen asynchronously where the data will be set now or in the future
+ // Callees are expected to query the underlying data with 'GetData()'
+ // from the 'CustomLoadableAsset' class to determine if the data is ready for use
+ std::shared_ptr<RawTextureAsset> LoadTexture(const CustomAssetLibrary::AssetID& asset_id,
+ std::shared_ptr<CustomAssetLibrary> library);
+
+ std::shared_ptr<GameTextureAsset> LoadGameTexture(const CustomAssetLibrary::AssetID& asset_id,
+ std::shared_ptr<CustomAssetLibrary> library);
+
+private:
+ // TODO C++20: use a 'derived_from' concept against 'CustomAsset' when available
+ template <typename AssetType>
+ std::shared_ptr<AssetType>
+ LoadOrCreateAsset(const CustomAssetLibrary::AssetID& asset_id,
+ std::map<CustomAssetLibrary::AssetID, std::weak_ptr<AssetType>>& asset_map,
+ std::shared_ptr<CustomAssetLibrary> library)
+ {
+ auto [it, inserted] = asset_map.try_emplace(asset_id);
+ if (!inserted)
+ return it->second.lock();
+ std::shared_ptr<AssetType> ptr(new AssetType(std::move(library), asset_id), [&](AssetType* a) {
+ asset_map.erase(a->GetAssetId());
+ m_total_bytes_loaded -= a->GetByteSizeInMemory();
+ std::lock_guard lk(m_assets_lock);
+ m_assets_to_monitor.erase(a->GetAssetId());
+ delete a;
+ });
+ it->second = ptr;
+ m_asset_load_thread.Push(it->second);
+ return ptr;
+ }
+
+ static constexpr auto TIME_BETWEEN_ASSET_MONITOR_CHECKS = std::chrono::milliseconds{500};
+ std::map<CustomAssetLibrary::AssetID, std::weak_ptr<RawTextureAsset>> m_textures;
+ std::map<CustomAssetLibrary::AssetID, std::weak_ptr<GameTextureAsset>> m_game_textures;
+ std::thread m_asset_monitor_thread;
+ Common::Flag m_asset_monitor_thread_shutdown;
+
+ std::size_t m_total_bytes_loaded = 0;
+ std::size_t m_max_memory_available = 0;
+
+ std::map<CustomAssetLibrary::AssetID, std::weak_ptr<CustomAsset>> m_assets_to_monitor;
+ std::mutex m_assets_lock;
+ Common::WorkQueueThread<std::weak_ptr<CustomAsset>> m_asset_load_thread;
+};
+} // namespace VideoCommon
diff --git a/Source/Core/VideoCommon/Assets/TextureAsset.cpp b/Source/Core/VideoCommon/Assets/TextureAsset.cpp
new file mode 100644
index 0000000000..fd27ee80b6
--- /dev/null
+++ b/Source/Core/VideoCommon/Assets/TextureAsset.cpp
@@ -0,0 +1,77 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "VideoCommon/Assets/TextureAsset.h"
+
+#include "Common/Logging/Log.h"
+
+namespace VideoCommon
+{
+CustomAssetLibrary::LoadInfo RawTextureAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id)
+{
+ std::lock_guard lk(m_lock);
+ const auto loaded_info = m_owning_library->LoadTexture(asset_id, &m_data);
+ if (loaded_info.m_bytes_loaded == 0)
+ return {};
+ m_loaded = true;
+ return loaded_info;
+}
+
+CustomAssetLibrary::LoadInfo GameTextureAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id)
+{
+ std::lock_guard lk(m_lock);
+ const auto loaded_info = m_owning_library->LoadGameTexture(asset_id, &m_data);
+ if (loaded_info.m_bytes_loaded == 0)
+ return {};
+ m_loaded = true;
+ return loaded_info;
+}
+
+bool GameTextureAsset::Validate(u32 native_width, u32 native_height) const
+{
+ std::lock_guard lk(m_lock);
+
+ if (!m_loaded)
+ {
+ ERROR_LOG_FMT(VIDEO,
+ "Game texture can't be validated for asset '{}' because it is not loaded yet.",
+ GetAssetId());
+ return false;
+ }
+
+ if (m_data.m_levels.empty())
+ {
+ ERROR_LOG_FMT(VIDEO,
+ "Game texture can't be validated for asset '{}' because no data was available.",
+ GetAssetId());
+ return false;
+ }
+
+ // Verify that the aspect ratio of the texture hasn't changed, as this could have
+ // side-effects.
+ const VideoCommon::CustomTextureData::Level& first_mip = m_data.m_levels[0];
+ if (first_mip.width * native_height != first_mip.height * native_width)
+ {
+ ERROR_LOG_FMT(
+ VIDEO,
+ "Invalid custom texture size {}x{} for game texture asset '{}'. The aspect differs "
+ "from the native size {}x{}.",
+ first_mip.width, first_mip.height, GetAssetId(), native_width, native_height);
+ return false;
+ }
+
+ // Same deal if the custom texture isn't a multiple of the native size.
+ if (native_width != 0 && native_height != 0 &&
+ (first_mip.width % native_width || first_mip.height % native_height))
+ {
+ ERROR_LOG_FMT(
+ VIDEO,
+ "Invalid custom texture size {}x{} for game texture asset '{}'. Please use an integer "
+ "upscaling factor based on the native size {}x{}.",
+ first_mip.width, first_mip.height, GetAssetId(), native_width, native_height);
+ return false;
+ }
+
+ return true;
+}
+} // namespace VideoCommon
diff --git a/Source/Core/VideoCommon/Assets/TextureAsset.h b/Source/Core/VideoCommon/Assets/TextureAsset.h
new file mode 100644
index 0000000000..4cb0001a58
--- /dev/null
+++ b/Source/Core/VideoCommon/Assets/TextureAsset.h
@@ -0,0 +1,32 @@
+// Copyright 2023 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "VideoCommon/Assets/CustomAsset.h"
+#include "VideoCommon/Assets/CustomTextureData.h"
+
+namespace VideoCommon
+{
+class RawTextureAsset final : public CustomLoadableAsset<CustomTextureData>
+{
+public:
+ using CustomLoadableAsset::CustomLoadableAsset;
+
+private:
+ CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) override;
+};
+
+class GameTextureAsset final : public CustomLoadableAsset<CustomTextureData>
+{
+public:
+ using CustomLoadableAsset::CustomLoadableAsset;
+
+ // Validates that the game texture matches the native dimensions provided
+ // Callees are expected to call this once the data is loaded
+ bool Validate(u32 native_width, u32 native_height) const;
+
+private:
+ CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) override;
+};
+} // namespace VideoCommon
diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt
index fa2811b410..ed8642fb1e 100644
--- a/Source/Core/VideoCommon/CMakeLists.txt
+++ b/Source/Core/VideoCommon/CMakeLists.txt
@@ -12,10 +12,14 @@ add_library(videocommon
Assets/CustomAsset.h
Assets/CustomAssetLibrary.cpp
Assets/CustomAssetLibrary.h
+ Assets/CustomAssetLoader.cpp
+ Assets/CustomAssetLoader.h
Assets/CustomTextureData.cpp
Assets/CustomTextureData.h
Assets/DirectFilesystemAssetLibrary.cpp
Assets/DirectFilesystemAssetLibrary.h
+ Assets/TextureAsset.cpp
+ Assets/TextureAsset.h
AsyncRequests.cpp
AsyncRequests.h
AsyncShaderCompiler.cpp