From 07307edd49497da157dcb29e5b32bb884b4f4b89 Mon Sep 17 00:00:00 2001 From: iwubcode Date: Fri, 2 Jun 2023 14:24:21 -0500 Subject: [PATCH] VideoCommon: add an asset library that loads directly from the filesystem --- Source/Core/DolphinLib.props | 2 + .../Assets/DirectFilesystemAssetLibrary.cpp | 145 ++++++++++++++++++ .../Assets/DirectFilesystemAssetLibrary.h | 37 +++++ Source/Core/VideoCommon/CMakeLists.txt | 2 + 4 files changed, 186 insertions(+) create mode 100644 Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp create mode 100644 Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index bc50241e60..5ae7b6fd67 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -634,6 +634,7 @@ + @@ -1246,6 +1247,7 @@ + diff --git a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp new file mode 100644 index 0000000000..912def9e6b --- /dev/null +++ b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp @@ -0,0 +1,145 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoCommon/Assets/DirectFilesystemAssetLibrary.h" + +#include + +#include "Common/FileUtil.h" +#include "Common/Logging/Log.h" +#include "Common/StringUtil.h" +#include "VideoCommon/Assets/CustomTextureData.h" + +namespace VideoCommon +{ +namespace +{ +std::size_t GetAssetSize(const CustomTextureData& data) +{ + std::size_t total = 0; + for (const auto& level : data.m_levels) + { + total += level.data.size(); + } + return total; +} +} // namespace +CustomAssetLibrary::TimeType +DirectFilesystemAssetLibrary::GetLastAssetWriteTime(const AssetID& asset_id) const +{ + if (auto iter = m_assetid_to_asset_map_path.find(asset_id); + iter != m_assetid_to_asset_map_path.end()) + { + const auto& asset_map_path = iter->second; + CustomAssetLibrary::TimeType max_entry; + for (const auto& [key, value] : asset_map_path) + { + const auto tp = std::filesystem::last_write_time(value); + if (tp > max_entry) + max_entry = tp; + } + return max_entry; + } + + return {}; +} + +CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadTexture(const AssetID& asset_id, + CustomTextureData* data) +{ + if (auto iter = m_assetid_to_asset_map_path.find(asset_id); + iter != m_assetid_to_asset_map_path.end()) + { + const auto& asset_map_path = iter->second; + + // Raw texture is expected to have one asset mapped + if (asset_map_path.empty() || asset_map_path.size() > 1) + return {}; + const auto& asset_path = asset_map_path.begin()->second; + + const auto last_loaded_time = std::filesystem::last_write_time(asset_path); + auto ext = asset_path.extension().string(); + Common::ToLower(&ext); + if (ext == ".dds") + { + LoadDDSTexture(data, asset_path.string()); + if (data->m_levels.empty()) [[unlikely]] + return {}; + if (!LoadMips(asset_path, data)) + return {}; + + return LoadInfo{GetAssetSize(*data), last_loaded_time}; + } + else if (ext == ".png") + { + LoadPNGTexture(data, asset_path.string()); + if (data->m_levels.empty()) [[unlikely]] + return {}; + if (!LoadMips(asset_path, data)) + return {}; + + return LoadInfo{GetAssetSize(*data), last_loaded_time}; + } + } + + return {}; +} + +void DirectFilesystemAssetLibrary::SetAssetIDMapData( + const AssetID& asset_id, std::map asset_path_map) +{ + m_assetid_to_asset_map_path[asset_id] = std::move(asset_path_map); +} + +bool DirectFilesystemAssetLibrary::LoadMips(const std::filesystem::path& asset_path, + CustomTextureData* data) +{ + if (!data) [[unlikely]] + return false; + + std::string path; + std::string filename; + std::string extension; + SplitPath(asset_path.string(), &path, &filename, &extension); + + std::string extension_lower = extension; + Common::ToLower(&extension_lower); + + // Load additional mip levels + for (u32 mip_level = static_cast(data->m_levels.size());; mip_level++) + { + const auto mip_level_filename = filename + fmt::format("_mip{}", mip_level); + + const auto full_path = path + mip_level_filename + extension; + if (!File::Exists(full_path)) + return true; + + VideoCommon::CustomTextureData::Level level; + if (extension_lower == ".dds") + { + if (!LoadDDSTexture(&level, full_path, mip_level)) + { + ERROR_LOG_FMT(VIDEO, "Custom mipmap '{}' failed to load", mip_level_filename); + return false; + } + } + else if (extension_lower == ".png") + { + if (!LoadPNGTexture(&level, full_path)) + { + ERROR_LOG_FMT(VIDEO, "Custom mipmap '{}' failed to load", mip_level_filename); + return false; + } + } + else + { + ERROR_LOG_FMT(VIDEO, "Custom mipmap '{}' has unsupported extension", mip_level_filename); + return false; + } + + data->m_levels.push_back(std::move(level)); + } + + return true; +} +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h new file mode 100644 index 0000000000..d04981dac3 --- /dev/null +++ b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h @@ -0,0 +1,37 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include +#include +#include +#include + +#include "VideoCommon/Assets/CustomAssetLibrary.h" + +namespace VideoCommon +{ +class CustomTextureData; + +// This class implements 'CustomAssetLibrary' and loads any assets +// directly from the filesystem +class DirectFilesystemAssetLibrary final : public CustomAssetLibrary +{ +public: + LoadInfo LoadTexture(const AssetID& asset_id, CustomTextureData* data) override; + + // Gets the latest time from amongst all the files in the asset map + TimeType GetLastAssetWriteTime(const AssetID& asset_id) const override; + + // 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, + std::map asset_path_map); + +private: + // Loads additional mip levels into the texture structure until _mip texture is not found + bool LoadMips(const std::filesystem::path& asset_path, CustomTextureData* data); + std::map> m_assetid_to_asset_map_path; +}; +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 7ad8f45681..fa2811b410 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -14,6 +14,8 @@ add_library(videocommon Assets/CustomAssetLibrary.h Assets/CustomTextureData.cpp Assets/CustomTextureData.h + Assets/DirectFilesystemAssetLibrary.cpp + Assets/DirectFilesystemAssetLibrary.h AsyncRequests.cpp AsyncRequests.h AsyncShaderCompiler.cpp