Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Merge pull request #11871 from iwubcode/custom-assets
VideoCommon: add custom asset implementation and asset library
- Loading branch information
Showing
6 changed files
with
265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| // Copyright 2023 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include "VideoCommon/Assets/CustomAsset.h" | ||
|
|
||
| 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) | ||
| { | ||
| } | ||
|
|
||
| bool CustomAsset::Load() | ||
| { | ||
| const auto load_information = LoadImpl(m_asset_id); | ||
| if (load_information.m_bytes_loaded > 0) | ||
| { | ||
| m_bytes_loaded = load_information.m_bytes_loaded; | ||
| m_last_loaded_time = load_information.m_load_time; | ||
| } | ||
| return load_information.m_bytes_loaded != 0; | ||
| } | ||
|
|
||
| CustomAssetLibrary::TimeType CustomAsset::GetLastWriteTime() const | ||
| { | ||
| return m_owning_library->GetLastAssetWriteTime(m_asset_id); | ||
| } | ||
|
|
||
| const CustomAssetLibrary::TimeType& CustomAsset::GetLastLoadedTime() const | ||
| { | ||
| return m_last_loaded_time; | ||
| } | ||
|
|
||
| const CustomAssetLibrary::AssetID& CustomAsset::GetAssetId() const | ||
| { | ||
| return m_asset_id; | ||
| } | ||
|
|
||
| std::size_t CustomAsset::GetByteSizeInMemory() const | ||
| { | ||
| return m_bytes_loaded; | ||
| } | ||
|
|
||
| } // namespace VideoCommon |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| // Copyright 2023 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "Common/CommonTypes.h" | ||
| #include "VideoCommon/Assets/CustomAssetLibrary.h" | ||
|
|
||
| #include <memory> | ||
| #include <mutex> | ||
| #include <optional> | ||
|
|
||
| namespace VideoCommon | ||
| { | ||
| // An abstract class that provides operations for loading | ||
| // data from a 'CustomAssetLibrary' | ||
| class CustomAsset | ||
| { | ||
| public: | ||
| CustomAsset(std::shared_ptr<CustomAssetLibrary> library, | ||
| const CustomAssetLibrary::AssetID& asset_id); | ||
| virtual ~CustomAsset() = default; | ||
| CustomAsset(const CustomAsset&) = default; | ||
| CustomAsset(CustomAsset&&) = default; | ||
| CustomAsset& operator=(const CustomAsset&) = default; | ||
| CustomAsset& operator=(CustomAsset&&) = default; | ||
|
|
||
| // Loads the asset from the library returning a pass/fail result | ||
| bool Load(); | ||
|
|
||
| // Queries the last time the asset was modified or standard epoch time | ||
| // if the asset hasn't been modified yet | ||
| CustomAssetLibrary::TimeType GetLastWriteTime() const; | ||
|
|
||
| // Returns the time that the data was last loaded | ||
| const CustomAssetLibrary::TimeType& GetLastLoadedTime() const; | ||
|
|
||
| // Returns an id that uniquely identifies this asset | ||
| const CustomAssetLibrary::AssetID& GetAssetId() const; | ||
|
|
||
| // A rough estimate of how much space this asset | ||
| // will take in memroy | ||
| std::size_t GetByteSizeInMemory() const; | ||
|
|
||
| protected: | ||
| const std::shared_ptr<CustomAssetLibrary> m_owning_library; | ||
|
|
||
| private: | ||
| virtual CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) = 0; | ||
| CustomAssetLibrary::AssetID m_asset_id; | ||
| std::size_t m_bytes_loaded = 0; | ||
| CustomAssetLibrary::TimeType m_last_loaded_time; | ||
| }; | ||
|
|
||
| // An abstract class that is expected to | ||
| // be the base class for all assets | ||
| // It provides a simple interface for | ||
| // verifying that an asset data of type | ||
| // 'UnderlyingType' is loaded by | ||
| // checking against 'GetData()' | ||
| template <typename UnderlyingType> | ||
| class CustomLoadableAsset : public CustomAsset | ||
| { | ||
| public: | ||
| using CustomAsset::CustomAsset; | ||
|
|
||
| const UnderlyingType* GetData() const | ||
| { | ||
| std::lock_guard lk(m_lock); | ||
| if (m_loaded) | ||
| return &m_data; | ||
| return nullptr; | ||
| } | ||
|
|
||
| protected: | ||
| bool m_loaded = false; | ||
| mutable std::mutex m_lock; | ||
| UnderlyingType m_data; | ||
| }; | ||
|
|
||
| } // namespace VideoCommon |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| // Copyright 2023 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include "VideoCommon/Assets/CustomAssetLibrary.h" | ||
|
|
||
| #include <algorithm> | ||
|
|
||
| #include "Common/Logging/Log.h" | ||
| #include "VideoCommon/GraphicsModSystem/Runtime/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::LoadInfo CustomAssetLibrary::LoadGameTexture(const AssetID& asset_id, | ||
| CustomTextureData* data) | ||
| { | ||
| const auto load_info = LoadTexture(asset_id, data); | ||
| if (load_info.m_bytes_loaded == 0) | ||
| return {}; | ||
|
|
||
| // Note: 'LoadTexture()' ensures we have a level loaded | ||
| const auto& first_mip = data->m_levels[0]; | ||
|
|
||
| // Verify that each mip level is the correct size (divide by 2 each time). | ||
| u32 current_mip_width = first_mip.width; | ||
| u32 current_mip_height = first_mip.height; | ||
| for (u32 mip_level = 1; mip_level < static_cast<u32>(data->m_levels.size()); mip_level++) | ||
| { | ||
| if (current_mip_width != 1 || current_mip_height != 1) | ||
| { | ||
| current_mip_width = std::max(current_mip_width / 2, 1u); | ||
| current_mip_height = std::max(current_mip_height / 2, 1u); | ||
|
|
||
| const VideoCommon::CustomTextureData::Level& level = data->m_levels[mip_level]; | ||
| if (current_mip_width == level.width && current_mip_height == level.height) | ||
| continue; | ||
|
|
||
| ERROR_LOG_FMT(VIDEO, | ||
| "Invalid custom game texture size {}x{} for texture asset {}. Mipmap level {} " | ||
| "must be {}x{}.", | ||
| level.width, level.height, asset_id, mip_level, current_mip_width, | ||
| current_mip_height); | ||
| } | ||
| else | ||
| { | ||
| // It is invalid to have more than a single 1x1 mipmap. | ||
| ERROR_LOG_FMT(VIDEO, | ||
| "Custom game texture {} has too many 1x1 mipmaps. Skipping extra levels.", | ||
| asset_id); | ||
| } | ||
|
|
||
| // Drop this mip level and any others after it. | ||
| while (data->m_levels.size() > mip_level) | ||
| data->m_levels.pop_back(); | ||
| } | ||
|
|
||
| // All levels have to have the same format. | ||
| if (std::any_of(data->m_levels.begin(), data->m_levels.end(), | ||
| [&first_mip](const VideoCommon::CustomTextureData::Level& l) { | ||
| return l.format != first_mip.format; | ||
| })) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Custom game texture {} has inconsistent formats across mip levels.", | ||
| asset_id); | ||
|
|
||
| return {}; | ||
| } | ||
|
|
||
| return load_info; | ||
| } | ||
| } // namespace VideoCommon |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| // Copyright 2023 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <chrono> | ||
| #include <filesystem> | ||
| #include <map> | ||
| #include <string> | ||
|
|
||
| namespace VideoCommon | ||
| { | ||
| class CustomTextureData; | ||
|
|
||
| // This class provides functionality to load | ||
| // specific data (like textures). Where this data | ||
| // is loaded from is implementation defined | ||
| class CustomAssetLibrary | ||
| { | ||
| public: | ||
| // TODO: this should be std::chrono::system_clock::time_point to | ||
| // support any type of loader where the time isn't from the filesystem | ||
| // but there's no way to convert filesystem times to system times | ||
| // without 'clock_cast', once our builders catch up | ||
| // to support 'clock_cast' we should update this | ||
| // For now, it's fine as a filesystem library is all that is | ||
| // available | ||
| using TimeType = std::filesystem::file_time_type; | ||
|
|
||
| // The AssetID is a unique identifier for a particular asset | ||
| using AssetID = std::string; | ||
|
|
||
| struct LoadInfo | ||
| { | ||
| std::size_t m_bytes_loaded; | ||
| CustomAssetLibrary::TimeType m_load_time; | ||
| }; | ||
|
|
||
| // Loads a texture, if there are no levels, bytes loaded will be empty | ||
| virtual LoadInfo LoadTexture(const AssetID& asset_id, CustomTextureData* data) = 0; | ||
|
|
||
| // Gets the last write time for a given asset id | ||
| virtual TimeType GetLastAssetWriteTime(const AssetID& asset_id) const = 0; | ||
|
|
||
| // Loads a texture as a game texture, providing additional checks like confirming | ||
| // each mip level size is correct and that the format is consistent across the data | ||
| LoadInfo LoadGameTexture(const AssetID& asset_id, CustomTextureData* data); | ||
| }; | ||
| } // namespace VideoCommon |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters