diff options
| author | Admiral H. Curtiss <pikachu025@gmail.com> | 2023-06-03 01:11:41 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-06-03 01:11:41 +0200 |
| commit | d03e09c8fd911d1b4e66184ceedbeca989b75a0c (patch) | |
| tree | c55f05ebbe08f611ab6eb44952ff389c3bdbdd9f /Source/Core | |
| parent | 4c9210b439c72e726d41229c73eff881412beb48 (diff) | |
| parent | c93940c6ee8194fa48663b9bcd0f153c9d72a3ea (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.props | 4 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/Assets/CustomAssetLoader.cpp | 93 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/Assets/CustomAssetLoader.h | 81 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/Assets/TextureAsset.cpp | 77 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/Assets/TextureAsset.h | 32 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/CMakeLists.txt | 4 |
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 |
