diff --git a/Source/Core/Core/System.cpp b/Source/Core/Core/System.cpp index 22681cac43..d22c058258 100644 --- a/Source/Core/Core/System.cpp +++ b/Source/Core/Core/System.cpp @@ -33,12 +33,12 @@ #include "IOS/USB/Emulated/Infinity.h" #include "IOS/USB/Emulated/Skylanders/Skylander.h" #include "IOS/USB/USBScanner.h" -#include "VideoCommon/Assets/CustomResourceManager.h" #include "VideoCommon/CommandProcessor.h" #include "VideoCommon/Fifo.h" #include "VideoCommon/GeometryShaderManager.h" #include "VideoCommon/PixelEngine.h" #include "VideoCommon/PixelShaderManager.h" +#include "VideoCommon/Resources/CustomResourceManager.h" #include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/XFStateManager.h" diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index f4a9c04a92..e8ca03d4bd 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -673,10 +673,11 @@ + + - @@ -752,6 +753,9 @@ + + + @@ -1340,8 +1344,8 @@ + - @@ -1402,6 +1406,9 @@ + + + diff --git a/Source/Core/VideoCommon/Assets/AssetListener.h b/Source/Core/VideoCommon/Assets/AssetListener.h new file mode 100644 index 0000000000..d543a2bb14 --- /dev/null +++ b/Source/Core/VideoCommon/Assets/AssetListener.h @@ -0,0 +1,23 @@ +// Copyright 2025 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace VideoCommon +{ +class AssetListener +{ +public: + AssetListener() = default; + virtual ~AssetListener() = default; + + AssetListener(const AssetListener&) = default; + AssetListener(AssetListener&&) = default; + AssetListener& operator=(const AssetListener&) = default; + AssetListener& operator=(AssetListener&&) = default; + + virtual void NotifyAssetLoadSuccess() = 0; + virtual void NotifyAssetLoadFailed() = 0; + virtual void AssetUnloaded() = 0; +}; +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Assets/CustomResourceManager.cpp b/Source/Core/VideoCommon/Assets/CustomAssetCache.cpp similarity index 61% rename from Source/Core/VideoCommon/Assets/CustomResourceManager.cpp rename to Source/Core/VideoCommon/Assets/CustomAssetCache.cpp index 6f8d3557bb..606f28d5fe 100644 --- a/Source/Core/VideoCommon/Assets/CustomResourceManager.cpp +++ b/Source/Core/VideoCommon/Assets/CustomAssetCache.cpp @@ -1,7 +1,7 @@ // Copyright 2025 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "VideoCommon/Assets/CustomResourceManager.h" +#include "VideoCommon/Assets/CustomAssetCache.h" #include "Common/Logging/Log.h" #include "Common/MemoryUtil.h" @@ -14,7 +14,7 @@ namespace VideoCommon { -void CustomResourceManager::Initialize() +void CustomAssetCache::Initialize() { // Use half of available system memory but leave at least 2GiB unused for system stability. constexpr size_t must_keep_unused = 2 * size_t(1024 * 1024 * 1024); @@ -28,19 +28,16 @@ void CustomResourceManager::Initialize() ERROR_LOG_FMT(VIDEO, "Not enough system memory for custom resources."); m_asset_loader.Initialize(); - - m_xfb_event = - GetVideoEvents().after_frame_event.Register([this](Core::System&) { XFBTriggered(); }); } -void CustomResourceManager::Shutdown() +void CustomAssetCache::Shutdown() { Reset(); m_asset_loader.Shutdown(); } -void CustomResourceManager::Reset() +void CustomAssetCache::Reset() { m_asset_loader.Reset(true); @@ -48,66 +45,27 @@ void CustomResourceManager::Reset() m_pending_assets = {}; m_asset_handle_to_data.clear(); m_asset_id_to_handle.clear(); - m_texture_data_asset_cache.clear(); m_dirty_assets.clear(); m_ram_used = 0; } -void CustomResourceManager::MarkAssetDirty(const CustomAssetLibrary::AssetID& asset_id) +void CustomAssetCache::MarkAssetDirty(const CustomAssetLibrary::AssetID& asset_id) { std::lock_guard guard(m_dirty_mutex); m_dirty_assets.insert(asset_id); } -CustomResourceManager::TextureTimePair CustomResourceManager::GetTextureDataFromAsset( - const CustomAssetLibrary::AssetID& asset_id, - std::shared_ptr library) +void CustomAssetCache::MarkAssetPending(CustomAsset* asset) { - auto& resource = m_texture_data_asset_cache[asset_id]; - if (resource.asset_data != nullptr && - resource.asset_data->load_status == AssetData::LoadStatus::ResourceDataAvailable) - { - m_active_assets.MakeAssetHighestPriority(resource.asset->GetHandle(), resource.asset); - return {resource.texture_data, resource.asset->GetLastLoadedTime()}; - } - - // If there is an error, don't try and load again until the error is fixed - if (resource.asset_data != nullptr && resource.asset_data->has_load_error) - return {}; - - LoadTextureDataAsset(asset_id, std::move(library), &resource); - m_active_assets.MakeAssetHighestPriority(resource.asset->GetHandle(), resource.asset); - - return {}; + m_pending_assets.MakeAssetHighestPriority(asset->GetHandle(), asset); } -void CustomResourceManager::LoadTextureDataAsset( - const CustomAssetLibrary::AssetID& asset_id, - std::shared_ptr library, InternalTextureDataResource* resource) +void CustomAssetCache::MarkAssetActive(CustomAsset* asset) { - if (!resource->asset) - { - resource->asset = - CreateAsset(asset_id, AssetData::AssetType::TextureData, std::move(library)); - resource->asset_data = &m_asset_handle_to_data[resource->asset->GetHandle()]; - } - - auto texture_data = resource->asset->GetData(); - if (!texture_data || resource->asset_data->load_status == AssetData::LoadStatus::PendingReload) - { - // Tell the system we are still interested in loading this asset - const auto asset_handle = resource->asset->GetHandle(); - m_pending_assets.MakeAssetHighestPriority(asset_handle, - m_asset_handle_to_data[asset_handle].asset.get()); - } - else if (resource->asset_data->load_status == AssetData::LoadStatus::LoadFinished) - { - resource->texture_data = std::move(texture_data); - resource->asset_data->load_status = AssetData::LoadStatus::ResourceDataAvailable; - } + m_active_assets.MakeAssetHighestPriority(asset->GetHandle(), asset); } -void CustomResourceManager::XFBTriggered() +void CustomAssetCache::Update() { ProcessDirtyAssets(); ProcessLoadedAssets(); @@ -127,7 +85,7 @@ void CustomResourceManager::XFBTriggered() m_asset_loader.ScheduleAssetsToLoad(m_pending_assets.Elements(), allowed_memory); } -void CustomResourceManager::ProcessDirtyAssets() +void CustomAssetCache::ProcessDirtyAssets() { decltype(m_dirty_assets) dirty_assets; @@ -154,7 +112,7 @@ void CustomResourceManager::ProcessDirtyAssets() } } -void CustomResourceManager::ProcessLoadedAssets() +void CustomAssetCache::ProcessLoadedAssets() { const auto load_results = m_asset_loader.TakeLoadResults(); @@ -189,10 +147,18 @@ void CustomResourceManager::ProcessLoadedAssets() m_active_assets.InsertAsset(handle, asset_data.asset.get()); asset_data.load_status = AssetData::LoadStatus::LoadFinished; } + + for (const auto& listener : asset_data.listeners) + { + if (load_successful) + listener->NotifyAssetLoadSuccess(); + else + listener->NotifyAssetLoadFailed(); + } } } -void CustomResourceManager::RemoveAssetsUntilBelowMemoryLimit() +void CustomAssetCache::RemoveAssetsUntilBelowMemoryLimit() { const u64 threshold_ram = m_max_ram_available * 8 / 10; @@ -209,11 +175,11 @@ void CustomResourceManager::RemoveAssetsUntilBelowMemoryLimit() AssetData& asset_data = m_asset_handle_to_data[asset->GetHandle()]; - // Remove the resource manager's cached entry with its asset data - if (asset_data.type == AssetData::AssetType::TextureData) + for (const auto& listener : asset_data.listeners) { - m_texture_data_asset_cache.erase(asset->GetAssetId()); + listener->AssetUnloaded(); } + // Remove the asset's copy const std::size_t bytes_unloaded = asset_data.asset->Unload(); m_ram_used -= bytes_unloaded; diff --git a/Source/Core/VideoCommon/Assets/CustomResourceManager.h b/Source/Core/VideoCommon/Assets/CustomAssetCache.h similarity index 72% rename from Source/Core/VideoCommon/Assets/CustomResourceManager.h rename to Source/Core/VideoCommon/Assets/CustomAssetCache.h index 6a2b5cbbe3..169c0faaf8 100644 --- a/Source/Core/VideoCommon/Assets/CustomResourceManager.h +++ b/Source/Core/VideoCommon/Assets/CustomAssetCache.h @@ -10,25 +10,73 @@ #include #include "Common/CommonTypes.h" -#include "Common/HookableEvent.h" +#include "VideoCommon/Assets/AssetListener.h" #include "VideoCommon/Assets/CustomAsset.h" #include "VideoCommon/Assets/CustomAssetLibrary.h" #include "VideoCommon/Assets/CustomAssetLoader.h" -#include "VideoCommon/Assets/CustomTextureData.h" namespace VideoCommon { -class TextureAsset; - -// The resource manager manages custom resources (textures, shaders, meshes) -// called assets. These assets are loaded using a priority system, +// The asset cache manages custom assets (textures, shaders, meshes). +// These assets are loaded using a priority system, // where assets requested more often gets loaded first. This system // also tracks memory usage and if memory usage goes over a calculated limit, // then assets will be purged with older assets being targeted first. -class CustomResourceManager +class CustomAssetCache { public: + // A generic interface to describe an asset + // and load state + struct AssetData + { + std::unique_ptr asset; + + std::vector listeners; + + CustomAsset::TimeType load_request_time = {}; + bool has_load_error = false; + + enum class LoadStatus + { + PendingReload, + LoadFinished, + Unloaded, + }; + LoadStatus load_status = LoadStatus::PendingReload; + }; + + template + T* CreateAsset(const CustomAssetLibrary::AssetID& asset_id, + std::shared_ptr library, AssetListener* listener) + { + const auto [it, added] = + m_asset_id_to_handle.try_emplace(asset_id, m_asset_handle_to_data.size()); + if (added) + { + AssetData asset_data; + asset_data.asset = std::make_unique(std::move(library), asset_id, it->second); + asset_data.load_request_time = {}; + asset_data.has_load_error = false; + + m_asset_handle_to_data.insert_or_assign(it->second, std::move(asset_data)); + } + + auto& asset_data_from_handle = m_asset_handle_to_data[it->second]; + asset_data_from_handle.listeners.push_back(listener); + asset_data_from_handle.load_status = AssetData::LoadStatus::PendingReload; + + return static_cast(asset_data_from_handle.asset.get()); + } + + AssetData* GetAssetData(const CustomAssetLibrary::AssetID& asset_id) + { + const auto it_handle = m_asset_id_to_handle.find(asset_id); + if (it_handle == m_asset_id_to_handle.end()) + return nullptr; + return &m_asset_handle_to_data[it_handle->second]; + } + void Initialize(); void Shutdown(); @@ -37,81 +85,21 @@ public: // Request that an asset be reloaded void MarkAssetDirty(const CustomAssetLibrary::AssetID& asset_id); - void XFBTriggered(); + // Notify the system that we are interested in this asset and + // are waiting for it to be loaded + void MarkAssetPending(CustomAsset* asset); - using TextureTimePair = std::pair, CustomAsset::TimeType>; + // Notify the system we are interested in this asset and + // it has seen activity + void MarkAssetActive(CustomAsset* asset); - // Returns a pair with the custom texture data and the time it was last loaded - // Callees are not expected to hold onto the shared_ptr as that will prevent - // the resource manager from being able to properly release data - TextureTimePair GetTextureDataFromAsset(const CustomAssetLibrary::AssetID& asset_id, - std::shared_ptr library); + void Update(); private: - // A generic interface to describe an assets' type - // and load state - struct AssetData - { - std::unique_ptr asset; - CustomAsset::TimeType load_request_time = {}; - bool has_load_error = false; - - enum class AssetType - { - TextureData - }; - AssetType type; - - enum class LoadStatus - { - PendingReload, - LoadFinished, - ResourceDataAvailable, - Unloaded, - }; - LoadStatus load_status = LoadStatus::PendingReload; - }; - - // A structure to represent some raw texture data - // (this data hasn't hit the GPU yet, used for custom textures) - struct InternalTextureDataResource - { - AssetData* asset_data = nullptr; - VideoCommon::TextureAsset* asset = nullptr; - std::shared_ptr texture_data; - }; - - void LoadTextureDataAsset(const CustomAssetLibrary::AssetID& asset_id, - std::shared_ptr library, - InternalTextureDataResource* resource); - void ProcessDirtyAssets(); void ProcessLoadedAssets(); void RemoveAssetsUntilBelowMemoryLimit(); - template - T* CreateAsset(const CustomAssetLibrary::AssetID& asset_id, AssetData::AssetType asset_type, - std::shared_ptr library) - { - const auto [it, added] = - m_asset_id_to_handle.try_emplace(asset_id, m_asset_handle_to_data.size()); - - if (added) - { - AssetData asset_data; - asset_data.asset = std::make_unique(library, asset_id, it->second); - asset_data.type = asset_type; - asset_data.load_request_time = {}; - asset_data.has_load_error = false; - - m_asset_handle_to_data.insert_or_assign(it->second, std::move(asset_data)); - } - auto& asset_data_from_handle = m_asset_handle_to_data[it->second]; - asset_data_from_handle.load_status = AssetData::LoadStatus::PendingReload; - - return static_cast(asset_data_from_handle.asset.get()); - } - // Maintains a priority-sorted list of assets. // Used to figure out which assets to load or unload first. // Most recently used assets get marked with highest priority. @@ -202,14 +190,10 @@ private: // A calculated amount of memory to avoid exceeding. u64 m_max_ram_available = 0; - std::map m_texture_data_asset_cache; - std::mutex m_dirty_mutex; std::set m_dirty_assets; CustomAssetLoader m_asset_loader; - - Common::EventHook m_xfb_event; }; } // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp index 5e400f3de5..91f3102c58 100644 --- a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp +++ b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp @@ -14,13 +14,13 @@ #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" #include "VideoCommon/Assets/TextureAsset.h" #include "VideoCommon/Assets/TextureAssetUtils.h" #include "VideoCommon/RenderState.h" +#include "VideoCommon/Resources/CustomResourceManager.h" namespace VideoCommon { diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 73b9221c4b..4830c67113 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -8,13 +8,14 @@ add_library(videocommon AbstractStagingTexture.h AbstractTexture.cpp AbstractTexture.h + Assets/AssetListener.h Assets/CustomAsset.cpp Assets/CustomAsset.h + Assets/CustomAssetCache.cpp + Assets/CustomAssetCache.h Assets/CustomAssetLibrary.h Assets/CustomAssetLoader.cpp Assets/CustomAssetLoader.h - Assets/CustomResourceManager.cpp - Assets/CustomResourceManager.h Assets/CustomTextureData.cpp Assets/CustomTextureData.h Assets/DirectFilesystemAssetLibrary.cpp @@ -145,6 +146,12 @@ add_library(videocommon Present.h RenderState.cpp RenderState.h + Resources/CustomResourceManager.cpp + Resources/CustomResourceManager.h + Resources/Resource.cpp + Resources/Resource.h + Resources/TextureDataResource.cpp + Resources/TextureDataResource.h ShaderCache.cpp ShaderCache.h ShaderCompileUtils.cpp diff --git a/Source/Core/VideoCommon/HiresTextures.cpp b/Source/Core/VideoCommon/HiresTextures.cpp index 8acc3aff03..12f59f5ff5 100644 --- a/Source/Core/VideoCommon/HiresTextures.cpp +++ b/Source/Core/VideoCommon/HiresTextures.cpp @@ -27,6 +27,7 @@ #include "VideoCommon/Assets/CustomAsset.h" #include "VideoCommon/Assets/DirectFilesystemAssetLibrary.h" #include "VideoCommon/OnScreenDisplay.h" +#include "VideoCommon/Resources/CustomResourceManager.h" #include "VideoCommon/VideoConfig.h" constexpr std::string_view s_format_prefix{"tex1_"}; @@ -191,7 +192,7 @@ HiresTexture::HiresTexture(bool has_arbitrary_mipmaps, std::string id) { } -VideoCommon::CustomResourceManager::TextureTimePair HiresTexture::LoadTexture() const +VideoCommon::TextureDataResource* HiresTexture::LoadTexture() const { auto& system = Core::System::GetInstance(); auto& custom_resource_manager = system.GetCustomResourceManager(); diff --git a/Source/Core/VideoCommon/HiresTextures.h b/Source/Core/VideoCommon/HiresTextures.h index e99a3fc966..f577aebd78 100644 --- a/Source/Core/VideoCommon/HiresTextures.h +++ b/Source/Core/VideoCommon/HiresTextures.h @@ -8,12 +8,13 @@ #include #include -#include "Common/CommonTypes.h" -#include "VideoCommon/Assets/CustomResourceManager.h" -#include "VideoCommon/Assets/CustomTextureData.h" -#include "VideoCommon/TextureConfig.h" #include "VideoCommon/TextureInfo.h" +namespace VideoCommon +{ +class TextureDataResource; +} + enum class TextureFormat; std::set GetTextureDirectoriesWithGameId(const std::string& root_directory, @@ -30,7 +31,7 @@ public: HiresTexture(bool has_arbitrary_mipmaps, std::string id); bool HasArbitraryMipmaps() const { return m_has_arbitrary_mipmaps; } - VideoCommon::CustomResourceManager::TextureTimePair LoadTexture() const; + VideoCommon::TextureDataResource* LoadTexture() const; const std::string& GetId() const { return m_id; } private: diff --git a/Source/Core/VideoCommon/Resources/CustomResourceManager.cpp b/Source/Core/VideoCommon/Resources/CustomResourceManager.cpp new file mode 100644 index 0000000000..35eeb0393a --- /dev/null +++ b/Source/Core/VideoCommon/Resources/CustomResourceManager.cpp @@ -0,0 +1,124 @@ +// Copyright 2025 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoCommon/Resources/CustomResourceManager.h" + +#include "VideoCommon/VideoEvents.h" + +namespace VideoCommon +{ +void CustomResourceManager::Initialize() +{ + m_asset_cache.Initialize(); + + m_xfb_event = + AfterFrameEvent::Register([this](Core::System&) { XFBTriggered(); }, "CustomResourceManager"); +} + +void CustomResourceManager::Shutdown() +{ + m_asset_cache.Shutdown(); + Reset(); +} + +void CustomResourceManager::Reset() +{ + m_texture_data_resources.clear(); + + m_asset_cache.Reset(); +} + +void CustomResourceManager::MarkAssetDirty(const CustomAssetLibrary::AssetID& asset_id) +{ + m_asset_cache.MarkAssetDirty(asset_id); +} + +void CustomResourceManager::XFBTriggered() +{ + m_asset_cache.Update(); +} + +TextureDataResource* CustomResourceManager::GetTextureDataFromAsset( + const CustomAssetLibrary::AssetID& asset_id, + std::shared_ptr library) +{ + const auto [it, added] = m_texture_data_resources.try_emplace(asset_id, nullptr); + if (added) + { + it->second = std::make_unique(CreateResourceContext(asset_id, library)); + } + ProcessResource(it->second.get()); + return it->second.get(); +} + +Resource::ResourceContext CustomResourceManager::CreateResourceContext( + const CustomAssetLibrary::AssetID& asset_id, + const std::shared_ptr& library) +{ + return Resource::ResourceContext{asset_id, library, &m_asset_cache, this}; +} + +void CustomResourceManager::ProcessResource(Resource* resource) +{ + resource->MarkAsActive(); + + const auto data_processed = resource->IsDataProcessed(); + if (data_processed == Resource::TaskComplete::Yes || + data_processed == Resource::TaskComplete::Error) + { + resource->MarkAsActive(); + if (data_processed == Resource::TaskComplete::Error) + return; + } + + // Early out if we're already at our end state + if (resource->GetState() == Resource::State::DataAvailable) + return; + + ProcessResourceState(resource); +} + +void CustomResourceManager::ProcessResourceState(Resource* resource) +{ + Resource::State next_state = resource->GetState(); + Resource::TaskComplete task_complete = Resource::TaskComplete::No; + switch (resource->GetState()) + { + case Resource::State::ReloadData: + resource->ResetData(); + task_complete = Resource::TaskComplete::Yes; + next_state = Resource::State::CollectingPrimaryData; + break; + case Resource::State::CollectingPrimaryData: + task_complete = resource->CollectPrimaryData(); + next_state = Resource::State::CollectingDependencyData; + if (task_complete == Resource::TaskComplete::No) + resource->MarkAsPending(); + break; + case Resource::State::CollectingDependencyData: + task_complete = resource->CollectDependencyData(); + next_state = Resource::State::ProcessingData; + break; + case Resource::State::ProcessingData: + task_complete = resource->ProcessData(); + next_state = Resource::State::DataAvailable; + }; + + if (task_complete == Resource::TaskComplete::Yes) + { + resource->m_state = next_state; + if (next_state == Resource::State::DataAvailable) + { + resource->m_data_processed = task_complete; + } + else + { + ProcessResourceState(resource); + } + } + else if (task_complete == Resource::TaskComplete::Error) + { + resource->m_data_processed = task_complete; + } +} +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Resources/CustomResourceManager.h b/Source/Core/VideoCommon/Resources/CustomResourceManager.h new file mode 100644 index 0000000000..512bd77712 --- /dev/null +++ b/Source/Core/VideoCommon/Resources/CustomResourceManager.h @@ -0,0 +1,46 @@ +// Copyright 2025 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "Common/HookableEvent.h" + +#include "VideoCommon/Assets/CustomAssetCache.h" +#include "VideoCommon/Resources/TextureDataResource.h" + +namespace VideoCommon +{ +class CustomResourceManager +{ +public: + void Initialize(); + void Shutdown(); + + void Reset(); + + // Request that an asset be reloaded + void MarkAssetDirty(const CustomAssetLibrary::AssetID& asset_id); + + void XFBTriggered(); + + TextureDataResource* + GetTextureDataFromAsset(const CustomAssetLibrary::AssetID& asset_id, + std::shared_ptr library); + +private: + Resource::ResourceContext + CreateResourceContext(const CustomAssetLibrary::AssetID& asset_id, + const std::shared_ptr& library); + void ProcessResource(Resource* resource); + void ProcessResourceState(Resource* resource); + CustomAssetCache m_asset_cache; + + std::map> + m_texture_data_resources; + + Common::EventHook m_xfb_event; +}; +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Resources/Resource.cpp b/Source/Core/VideoCommon/Resources/Resource.cpp new file mode 100644 index 0000000000..52d1d8b01c --- /dev/null +++ b/Source/Core/VideoCommon/Resources/Resource.cpp @@ -0,0 +1,81 @@ +// Copyright 2025 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoCommon/Resources/Resource.h" + +namespace VideoCommon +{ +Resource::Resource(ResourceContext resource_context) + : m_resource_context(std::move(resource_context)) +{ +} + +void Resource::NotifyAssetChanged(bool has_error) +{ + m_data_processed = has_error ? TaskComplete::Error : TaskComplete::No; + m_state = State::ReloadData; + + for (Resource* reference : m_references) + { + reference->NotifyAssetChanged(has_error); + } +} + +void Resource::NotifyAssetUnloaded() +{ + OnUnloadRequested(); + + for (Resource* reference : m_references) + { + reference->NotifyAssetUnloaded(); + } +} + +void Resource::AddReference(Resource* reference) +{ + m_references.insert(reference); +} + +void Resource::RemoveReference(Resource* reference) +{ + m_references.erase(reference); +} + +void Resource::NotifyAssetLoadSuccess() +{ + NotifyAssetChanged(false); +} + +void Resource::NotifyAssetLoadFailed() +{ + NotifyAssetChanged(true); +} + +void Resource::AssetUnloaded() +{ + NotifyAssetUnloaded(); +} + +void Resource::OnUnloadRequested() +{ +} + +void Resource::ResetData() +{ +} + +Resource::TaskComplete Resource::CollectPrimaryData() +{ + return TaskComplete::Yes; +} + +Resource::TaskComplete Resource::CollectDependencyData() +{ + return TaskComplete::Yes; +} + +Resource::TaskComplete Resource::ProcessData() +{ + return TaskComplete::Yes; +} +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Resources/Resource.h b/Source/Core/VideoCommon/Resources/Resource.h new file mode 100644 index 0000000000..f9656fe2fa --- /dev/null +++ b/Source/Core/VideoCommon/Resources/Resource.h @@ -0,0 +1,82 @@ +// Copyright 2025 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "VideoCommon/Assets/AssetListener.h" +#include "VideoCommon/Assets/CustomAssetLibrary.h" + +#include +#include + +class AbstractTexture; +namespace VideoCommon +{ +class CustomAssetCache; +class CustomResourceManager; + +// A resource is an abstract object that maintains +// relationships between assets (ex: a material that references a texture), +// as well as a standard way of calculating the final data (ex: a material's AbstractPipeline) +class Resource : public AssetListener +{ +public: + // Everything the resource needs to manage itself + struct ResourceContext + { + CustomAssetLibrary::AssetID primary_asset_id; + std::shared_ptr asset_library; + CustomAssetCache* asset_cache; + CustomResourceManager* resource_manager; + }; + explicit Resource(ResourceContext resource_context); + + enum class TaskComplete + { + Yes, + No, + Error, + }; + + enum class State + { + ReloadData, + CollectingPrimaryData, + CollectingDependencyData, + ProcessingData, + DataAvailable, + }; + + TaskComplete IsDataProcessed() const { return m_data_processed; } + State GetState() const { return m_state; } + + void AddReference(Resource* reference); + void RemoveReference(Resource* reference); + + virtual void MarkAsActive() = 0; + virtual void MarkAsPending() = 0; + +protected: + ResourceContext m_resource_context; + +private: + void NotifyAssetChanged(bool has_error); + void NotifyAssetUnloaded(); + + void NotifyAssetLoadSuccess() final; + void NotifyAssetLoadFailed() final; + void AssetUnloaded() final; + virtual void OnUnloadRequested(); + + friend class CustomResourceManager; + virtual void ResetData(); + virtual TaskComplete CollectPrimaryData(); + virtual TaskComplete CollectDependencyData(); + virtual TaskComplete ProcessData(); + + TaskComplete m_data_processed = TaskComplete::No; + State m_state = State::ReloadData; + + std::unordered_set m_references; +}; +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Resources/TextureDataResource.cpp b/Source/Core/VideoCommon/Resources/TextureDataResource.cpp new file mode 100644 index 0000000000..d72ae8d39d --- /dev/null +++ b/Source/Core/VideoCommon/Resources/TextureDataResource.cpp @@ -0,0 +1,48 @@ +// Copyright 2025 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoCommon/Resources/TextureDataResource.h" + +#include "VideoCommon/Assets/CustomAssetCache.h" + +namespace VideoCommon +{ +TextureDataResource::TextureDataResource(Resource::ResourceContext resource_context) + : Resource(std::move(resource_context)) +{ + m_texture_asset = m_resource_context.asset_cache->CreateAsset( + m_resource_context.primary_asset_id, m_resource_context.asset_library, this); +} + +std::shared_ptr TextureDataResource::GetData() const +{ + return m_current_texture_data; +} + +CustomAsset::TimeType TextureDataResource::GetLoadTime() const +{ + return m_current_time; +} + +Resource::TaskComplete TextureDataResource::CollectPrimaryData() +{ + const auto last_load_time = m_texture_asset->GetLastLoadedTime(); + const auto asset = m_texture_asset->GetData(); + if (!asset) + return Resource::TaskComplete::No; + + m_current_texture_data = asset; + m_current_time = last_load_time; + return Resource::TaskComplete::Yes; +} + +void TextureDataResource::MarkAsActive() +{ + m_resource_context.asset_cache->MarkAssetActive(m_texture_asset); +} + +void TextureDataResource::MarkAsPending() +{ + m_resource_context.asset_cache->MarkAssetPending(m_texture_asset); +} +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/Resources/TextureDataResource.h b/Source/Core/VideoCommon/Resources/TextureDataResource.h new file mode 100644 index 0000000000..edb529d407 --- /dev/null +++ b/Source/Core/VideoCommon/Resources/TextureDataResource.h @@ -0,0 +1,33 @@ +// Copyright 2025 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "VideoCommon/Resources/Resource.h" + +#include "VideoCommon/Assets/CustomTextureData.h" +#include "VideoCommon/Assets/TextureAsset.h" + +namespace VideoCommon +{ +class TextureDataResource final : public Resource +{ +public: + explicit TextureDataResource(Resource::ResourceContext resource_context); + + std::shared_ptr GetData() const; + CustomAsset::TimeType GetLoadTime() const; + + void MarkAsActive() override; + void MarkAsPending() override; + +private: + TaskComplete CollectPrimaryData() override; + + // Note: asset cache owns the asset, we access as a reference + TextureAsset* m_texture_asset = nullptr; + + std::shared_ptr m_current_texture_data; + CustomAsset::TimeType m_current_time = {}; +}; +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index ce27429381..6ae8b4f96f 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -4,6 +4,7 @@ #include "VideoCommon/TextureCacheBase.h" #include +#include #include #include #include @@ -37,7 +38,6 @@ #include "VideoCommon/AbstractFramebuffer.h" #include "VideoCommon/AbstractGfx.h" #include "VideoCommon/AbstractStagingTexture.h" -#include "VideoCommon/Assets/CustomResourceManager.h" #include "VideoCommon/Assets/CustomTextureData.h" #include "VideoCommon/Assets/TextureAssetUtils.h" #include "VideoCommon/BPMemory.h" @@ -48,6 +48,7 @@ #include "VideoCommon/OpcodeDecoding.h" #include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/Present.h" +#include "VideoCommon/Resources/CustomResourceManager.h" #include "VideoCommon/ShaderCache.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/TMEM.h" @@ -266,9 +267,9 @@ bool TextureCacheBase::DidLinkedAssetsChange(const TCacheEntry& entry) if (!entry.hires_texture) return false; - const auto [texture_data, load_time] = entry.hires_texture->LoadTexture(); + const auto* resource = entry.hires_texture->LoadTexture(); - return load_time > entry.last_load_time; + return resource->GetLoadTime() > entry.last_load_time; } RcTcacheEntry TextureCacheBase::ApplyPaletteToEntry(RcTcacheEntry& entry, const u8* palette, @@ -1569,7 +1570,9 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp if (hires_texture) { has_arbitrary_mipmaps = hires_texture->HasArbitraryMipmaps(); - std::tie(custom_texture_data, load_time) = hires_texture->LoadTexture(); + const auto resource = hires_texture->LoadTexture(); + load_time = resource->GetLoadTime(); + custom_texture_data = resource->GetData(); if (custom_texture_data && !VideoCommon::ValidateTextureData( hires_texture->GetId(), *custom_texture_data, texture_info.GetRawWidth(), texture_info.GetRawHeight())) diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 23d9d6e7f4..7eeefb7187 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -43,7 +43,6 @@ #endif #include "VideoCommon/AbstractGfx.h" -#include "VideoCommon/Assets/CustomResourceManager.h" #include "VideoCommon/AsyncRequests.h" #include "VideoCommon/BPStructs.h" #include "VideoCommon/BoundingBox.h" @@ -59,6 +58,7 @@ #include "VideoCommon/PixelEngine.h" #include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/Present.h" +#include "VideoCommon/Resources/CustomResourceManager.h" #include "VideoCommon/TMEM.h" #include "VideoCommon/TextureCacheBase.h" #include "VideoCommon/VertexLoaderManager.h"