Fixed resource loader
This commit is contained in:
@ -57,6 +57,7 @@ set(TETRIS_SOURCES
|
|||||||
src/graphics/renderers/SyncLineRenderer.cpp
|
src/graphics/renderers/SyncLineRenderer.cpp
|
||||||
src/graphics/renderers/UIRenderer.cpp
|
src/graphics/renderers/UIRenderer.cpp
|
||||||
src/audio/Audio.cpp
|
src/audio/Audio.cpp
|
||||||
|
src/renderer/SDLRenderer.cpp
|
||||||
src/gameplay/effects/LineEffect.cpp
|
src/gameplay/effects/LineEffect.cpp
|
||||||
src/audio/SoundEffect.cpp
|
src/audio/SoundEffect.cpp
|
||||||
src/video/VideoPlayer.cpp
|
src/video/VideoPlayer.cpp
|
||||||
@ -66,6 +67,7 @@ set(TETRIS_SOURCES
|
|||||||
src/app/Fireworks.cpp
|
src/app/Fireworks.cpp
|
||||||
src/app/AssetLoader.cpp
|
src/app/AssetLoader.cpp
|
||||||
src/app/TextureLoader.cpp
|
src/app/TextureLoader.cpp
|
||||||
|
src/resources/ResourceManager.cpp
|
||||||
src/states/LoadingManager.cpp
|
src/states/LoadingManager.cpp
|
||||||
# State implementations (new)
|
# State implementations (new)
|
||||||
src/states/LoadingState.cpp
|
src/states/LoadingState.cpp
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
#include "app/AssetLoader.h"
|
#include "app/AssetLoader.h"
|
||||||
#include <SDL3_image/SDL_image.h>
|
#include <SDL3_image/SDL_image.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include "app/TextureLoader.h"
|
||||||
|
|
||||||
|
#include "utils/ImagePathResolver.h"
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
AssetLoader::AssetLoader() = default;
|
AssetLoader::AssetLoader() = default;
|
||||||
|
|
||||||
@ -37,6 +41,10 @@ void AssetLoader::shutdown() {
|
|||||||
m_renderer = nullptr;
|
m_renderer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AssetLoader::setResourceManager(resources::ResourceManager* mgr) {
|
||||||
|
m_resourceManager = mgr;
|
||||||
|
}
|
||||||
|
|
||||||
void AssetLoader::setBasePath(const std::string& basePath) {
|
void AssetLoader::setBasePath(const std::string& basePath) {
|
||||||
m_basePath = basePath;
|
m_basePath = basePath;
|
||||||
}
|
}
|
||||||
@ -65,16 +73,18 @@ bool AssetLoader::performStep() {
|
|||||||
|
|
||||||
std::string fullPath = m_basePath.empty() ? path : (m_basePath + "/" + path);
|
std::string fullPath = m_basePath.empty() ? path : (m_basePath + "/" + path);
|
||||||
|
|
||||||
SDL_Surface* surf = IMG_Load(fullPath.c_str());
|
// Diagnostic: resolve path and check file existence
|
||||||
if (!surf) {
|
const std::string resolved = AssetPath::resolveImagePath(path);
|
||||||
std::lock_guard<std::mutex> lk(m_errorsMutex);
|
bool exists = false;
|
||||||
m_errors.push_back(std::string("IMG_Load failed: ") + fullPath + " -> " + SDL_GetError());
|
try { if (!resolved.empty()) exists = std::filesystem::exists(std::filesystem::u8path(resolved)); } catch (...) { exists = false; }
|
||||||
} else {
|
|
||||||
SDL_Texture* tex = SDL_CreateTextureFromSurface(m_renderer, surf);
|
// Use TextureLoader to centralize loading and ResourceManager caching
|
||||||
SDL_DestroySurface(surf);
|
TextureLoader loader(m_loadedTasks, m_currentLoading, m_currentLoadingMutex, m_errors, m_errorsMutex);
|
||||||
|
loader.setResourceManager(m_resourceManager);
|
||||||
|
// Pass the original queued path (not the full resolved path) so caching keys stay consistent
|
||||||
|
SDL_Texture* tex = loader.loadFromImage(m_renderer, path);
|
||||||
if (!tex) {
|
if (!tex) {
|
||||||
std::lock_guard<std::mutex> lk(m_errorsMutex);
|
// errors have been recorded by TextureLoader
|
||||||
m_errors.push_back(std::string("CreateTexture failed: ") + fullPath);
|
|
||||||
} else {
|
} else {
|
||||||
std::lock_guard<std::mutex> lk(m_texturesMutex);
|
std::lock_guard<std::mutex> lk(m_texturesMutex);
|
||||||
auto& slot = m_textures[path];
|
auto& slot = m_textures[path];
|
||||||
@ -83,7 +93,6 @@ bool AssetLoader::performStep() {
|
|||||||
}
|
}
|
||||||
slot = tex;
|
slot = tex;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
m_loadedTasks.fetch_add(1, std::memory_order_relaxed);
|
m_loadedTasks.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
|
||||||
@ -104,12 +113,17 @@ void AssetLoader::adoptTexture(const std::string& path, SDL_Texture* texture) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// register in local map and resource manager
|
||||||
std::lock_guard<std::mutex> lk(m_texturesMutex);
|
std::lock_guard<std::mutex> lk(m_texturesMutex);
|
||||||
auto& slot = m_textures[path];
|
auto& slot = m_textures[path];
|
||||||
if (slot && slot != texture) {
|
if (slot && slot != texture) {
|
||||||
SDL_DestroyTexture(slot);
|
SDL_DestroyTexture(slot);
|
||||||
}
|
}
|
||||||
slot = texture;
|
slot = texture;
|
||||||
|
if (m_resourceManager) {
|
||||||
|
std::shared_ptr<void> sp(texture, [](void* t){ SDL_DestroyTexture(static_cast<SDL_Texture*>(t)); });
|
||||||
|
m_resourceManager->put(path, sp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float AssetLoader::getProgress() const {
|
float AssetLoader::getProgress() const {
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include "../resources/ResourceManager.h"
|
||||||
|
|
||||||
// Lightweight AssetLoader scaffold.
|
// Lightweight AssetLoader scaffold.
|
||||||
// Responsibilities:
|
// Responsibilities:
|
||||||
@ -22,6 +23,7 @@ public:
|
|||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
void setBasePath(const std::string& basePath);
|
void setBasePath(const std::string& basePath);
|
||||||
|
void setResourceManager(resources::ResourceManager* mgr);
|
||||||
|
|
||||||
// Queue a texture path (relative to base path) for loading.
|
// Queue a texture path (relative to base path) for loading.
|
||||||
void queueTexture(const std::string& path);
|
void queueTexture(const std::string& path);
|
||||||
@ -49,6 +51,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
SDL_Renderer* m_renderer = nullptr;
|
SDL_Renderer* m_renderer = nullptr;
|
||||||
std::string m_basePath;
|
std::string m_basePath;
|
||||||
|
resources::ResourceManager* m_resourceManager = nullptr;
|
||||||
|
|
||||||
// queued paths (simple FIFO)
|
// queued paths (simple FIFO)
|
||||||
std::vector<std::string> m_queue;
|
std::vector<std::string> m_queue;
|
||||||
|
|||||||
@ -68,6 +68,7 @@
|
|||||||
#include "ui/MenuLayout.h"
|
#include "ui/MenuLayout.h"
|
||||||
|
|
||||||
#include "utils/ImagePathResolver.h"
|
#include "utils/ImagePathResolver.h"
|
||||||
|
#include "../resources/ResourceManager.h"
|
||||||
|
|
||||||
// ---------- Game config ----------
|
// ---------- Game config ----------
|
||||||
static constexpr int LOGICAL_W = 1200;
|
static constexpr int LOGICAL_W = 1200;
|
||||||
@ -187,6 +188,7 @@ struct TetrisApp::Impl {
|
|||||||
AssetLoader assetLoader;
|
AssetLoader assetLoader;
|
||||||
std::unique_ptr<LoadingManager> loadingManager;
|
std::unique_ptr<LoadingManager> loadingManager;
|
||||||
std::unique_ptr<TextureLoader> textureLoader;
|
std::unique_ptr<TextureLoader> textureLoader;
|
||||||
|
resources::ResourceManager resourceManager;
|
||||||
|
|
||||||
FontAtlas pixelFont;
|
FontAtlas pixelFont;
|
||||||
FontAtlas font;
|
FontAtlas font;
|
||||||
@ -427,6 +429,8 @@ int TetrisApp::Impl::init()
|
|||||||
|
|
||||||
// Asset loader (creates SDL_Textures on the main thread)
|
// Asset loader (creates SDL_Textures on the main thread)
|
||||||
assetLoader.init(renderer);
|
assetLoader.init(renderer);
|
||||||
|
// Wire resource manager into loader so textures are cached and reused
|
||||||
|
assetLoader.setResourceManager(&resourceManager);
|
||||||
loadingManager = std::make_unique<LoadingManager>(&assetLoader);
|
loadingManager = std::make_unique<LoadingManager>(&assetLoader);
|
||||||
|
|
||||||
// Legacy image loader (used only as a fallback when AssetLoader misses)
|
// Legacy image loader (used only as a fallback when AssetLoader misses)
|
||||||
@ -436,6 +440,8 @@ int TetrisApp::Impl::init()
|
|||||||
currentLoadingMutex,
|
currentLoadingMutex,
|
||||||
assetLoadErrors,
|
assetLoadErrors,
|
||||||
assetLoadErrorsMutex);
|
assetLoadErrorsMutex);
|
||||||
|
// Let legacy TextureLoader access the same resource cache
|
||||||
|
textureLoader->setResourceManager(&resourceManager);
|
||||||
|
|
||||||
// Load scores asynchronously but keep the worker alive until shutdown
|
// Load scores asynchronously but keep the worker alive until shutdown
|
||||||
scoreLoader = std::jthread([this]() {
|
scoreLoader = std::jthread([this]() {
|
||||||
@ -1785,6 +1791,8 @@ void TetrisApp::Impl::runLoop()
|
|||||||
nextPanelTex = assetLoader.getTexture(Assets::NEXT_PANEL);
|
nextPanelTex = assetLoader.getTexture(Assets::NEXT_PANEL);
|
||||||
holdPanelTex = assetLoader.getTexture(Assets::HOLD_PANEL);
|
holdPanelTex = assetLoader.getTexture(Assets::HOLD_PANEL);
|
||||||
|
|
||||||
|
// texture retrieval diagnostics removed
|
||||||
|
|
||||||
auto ensureTextureSize = [&](SDL_Texture* tex, int& outW, int& outH) {
|
auto ensureTextureSize = [&](SDL_Texture* tex, int& outW, int& outH) {
|
||||||
if (!tex) return;
|
if (!tex) return;
|
||||||
if (outW > 0 && outH > 0) return;
|
if (outW > 0 && outH > 0) return;
|
||||||
|
|||||||
@ -6,6 +6,8 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#include "utils/ImagePathResolver.h"
|
#include "utils/ImagePathResolver.h"
|
||||||
|
|
||||||
TextureLoader::TextureLoader(
|
TextureLoader::TextureLoader(
|
||||||
@ -45,6 +47,18 @@ SDL_Texture* TextureLoader::loadFromImage(SDL_Renderer* renderer, const std::str
|
|||||||
const std::string resolvedPath = AssetPath::resolveImagePath(path);
|
const std::string resolvedPath = AssetPath::resolveImagePath(path);
|
||||||
setCurrentLoadingFile(resolvedPath.empty() ? path : resolvedPath);
|
setCurrentLoadingFile(resolvedPath.empty() ? path : resolvedPath);
|
||||||
|
|
||||||
|
// Check filesystem existence for diagnostics (no console log)
|
||||||
|
bool fileExists = false;
|
||||||
|
try { if (!resolvedPath.empty()) fileExists = std::filesystem::exists(std::filesystem::u8path(resolvedPath)); } catch (...) { fileExists = false; }
|
||||||
|
// If resource manager provided, check cache first using the original asset key (path)
|
||||||
|
if (resourceManager_) {
|
||||||
|
if (auto sp = resourceManager_->get<SDL_Texture>(path)) {
|
||||||
|
clearCurrentLoadingFile();
|
||||||
|
loadedTasks_.fetch_add(1);
|
||||||
|
return sp.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SDL_Surface* surface = IMG_Load(resolvedPath.c_str());
|
SDL_Surface* surface = IMG_Load(resolvedPath.c_str());
|
||||||
if (!surface) {
|
if (!surface) {
|
||||||
{
|
{
|
||||||
@ -54,7 +68,7 @@ SDL_Texture* TextureLoader::loadFromImage(SDL_Renderer* renderer, const std::str
|
|||||||
}
|
}
|
||||||
loadedTasks_.fetch_add(1);
|
loadedTasks_.fetch_add(1);
|
||||||
clearCurrentLoadingFile();
|
clearCurrentLoadingFile();
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load image %s (resolved: %s): %s", path.c_str(), resolvedPath.c_str(), SDL_GetError());
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load image %s (resolved: %s) exists=%s: %s", path.c_str(), resolvedPath.c_str(), fileExists ? "yes" : "no", SDL_GetError());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +80,7 @@ SDL_Texture* TextureLoader::loadFromImage(SDL_Renderer* renderer, const std::str
|
|||||||
}
|
}
|
||||||
|
|
||||||
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
|
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||||
|
// surface size preserved in outW/outH; no console log
|
||||||
SDL_DestroySurface(surface);
|
SDL_DestroySurface(surface);
|
||||||
|
|
||||||
if (!texture) {
|
if (!texture) {
|
||||||
@ -80,6 +95,15 @@ SDL_Texture* TextureLoader::loadFromImage(SDL_Renderer* renderer, const std::str
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No texture-size console diagnostics here
|
||||||
|
|
||||||
|
// cache in resource manager if present
|
||||||
|
if (resourceManager_) {
|
||||||
|
std::shared_ptr<void> sp(texture, [](void* t){ SDL_DestroyTexture(static_cast<SDL_Texture*>(t)); });
|
||||||
|
// store under original asset key (path) so callers using logical asset names find them
|
||||||
|
resourceManager_->put(path, sp);
|
||||||
|
}
|
||||||
|
|
||||||
loadedTasks_.fetch_add(1);
|
loadedTasks_.fetch_add(1);
|
||||||
clearCurrentLoadingFile();
|
clearCurrentLoadingFile();
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "../resources/ResourceManager.h"
|
||||||
|
|
||||||
class TextureLoader {
|
class TextureLoader {
|
||||||
public:
|
public:
|
||||||
@ -16,6 +17,8 @@ public:
|
|||||||
std::vector<std::string>& assetLoadErrors,
|
std::vector<std::string>& assetLoadErrors,
|
||||||
std::mutex& assetLoadErrorsMutex);
|
std::mutex& assetLoadErrorsMutex);
|
||||||
|
|
||||||
|
void setResourceManager(resources::ResourceManager* mgr) { resourceManager_ = mgr; }
|
||||||
|
|
||||||
SDL_Texture* loadFromImage(SDL_Renderer* renderer, const std::string& path, int* outW = nullptr, int* outH = nullptr);
|
SDL_Texture* loadFromImage(SDL_Renderer* renderer, const std::string& path, int* outW = nullptr, int* outH = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -28,4 +31,6 @@ private:
|
|||||||
void setCurrentLoadingFile(const std::string& filename);
|
void setCurrentLoadingFile(const std::string& filename);
|
||||||
void clearCurrentLoadingFile();
|
void clearCurrentLoadingFile();
|
||||||
void recordAssetLoadError(const std::string& message);
|
void recordAssetLoadError(const std::string& message);
|
||||||
|
|
||||||
|
resources::ResourceManager* resourceManager_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,6 +7,8 @@
|
|||||||
#include <SDL3_ttf/SDL_ttf.h>
|
#include <SDL3_ttf/SDL_ttf.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include "../../utils/ImagePathResolver.h"
|
#include "../../utils/ImagePathResolver.h"
|
||||||
|
#include "../../core/Config.h"
|
||||||
|
#include "../../resources/AssetPaths.h"
|
||||||
|
|
||||||
AssetManager::AssetManager()
|
AssetManager::AssetManager()
|
||||||
: m_renderer(nullptr)
|
: m_renderer(nullptr)
|
||||||
@ -103,7 +105,34 @@ SDL_Texture* AssetManager::loadTexture(const std::string& id, const std::string&
|
|||||||
|
|
||||||
SDL_Texture* AssetManager::getTexture(const std::string& id) const {
|
SDL_Texture* AssetManager::getTexture(const std::string& id) const {
|
||||||
auto it = m_textures.find(id);
|
auto it = m_textures.find(id);
|
||||||
return (it != m_textures.end()) ? it->second : nullptr;
|
if (it != m_textures.end()) return it->second;
|
||||||
|
|
||||||
|
// Lazy fallback: attempt to load well-known short ids from configured asset paths.
|
||||||
|
std::vector<std::string> candidates;
|
||||||
|
if (id == "logo") {
|
||||||
|
candidates.push_back(std::string(Assets::LOGO));
|
||||||
|
candidates.push_back(Config::Assets::LOGO_BMP);
|
||||||
|
} else if (id == "logo_small") {
|
||||||
|
candidates.push_back(Config::Assets::LOGO_SMALL_BMP);
|
||||||
|
candidates.push_back(std::string(Assets::LOGO));
|
||||||
|
} else if (id == "background") {
|
||||||
|
candidates.push_back(std::string(Assets::MAIN_SCREEN));
|
||||||
|
candidates.push_back(Config::Assets::BACKGROUND_BMP);
|
||||||
|
} else if (id == "blocks") {
|
||||||
|
candidates.push_back(std::string(Assets::BLOCKS_SPRITE));
|
||||||
|
candidates.push_back(Config::Assets::BLOCKS_BMP);
|
||||||
|
} else if (id == "asteroids") {
|
||||||
|
candidates.push_back(std::string(Assets::ASTEROID_SPRITE));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &candidatePath : candidates) {
|
||||||
|
if (candidatePath.empty()) continue;
|
||||||
|
AssetManager* self = const_cast<AssetManager*>(this);
|
||||||
|
SDL_Texture* tex = self->loadTexture(id, candidatePath);
|
||||||
|
if (tex) return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AssetManager::unloadTexture(const std::string& id) {
|
bool AssetManager::unloadTexture(const std::string& id) {
|
||||||
|
|||||||
20
src/renderer/Renderer.h
Normal file
20
src/renderer/Renderer.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace renderer {
|
||||||
|
|
||||||
|
class Renderer {
|
||||||
|
public:
|
||||||
|
virtual ~Renderer() = default;
|
||||||
|
|
||||||
|
// Wrap common operations used by renderers
|
||||||
|
virtual SDL_Texture* createTextureFromSurface(SDL_Surface* surf) = 0;
|
||||||
|
virtual void destroyTexture(SDL_Texture* tex) = 0;
|
||||||
|
virtual void copy(SDL_Texture* tex, const SDL_Rect* src, const SDL_Rect* dst) = 0;
|
||||||
|
virtual void clear(const SDL_Color& color) = 0;
|
||||||
|
virtual void present() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace renderer
|
||||||
46
src/renderer/SDLRenderer.cpp
Normal file
46
src/renderer/SDLRenderer.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#include "Renderer.h"
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
namespace renderer {
|
||||||
|
|
||||||
|
class SDLRendererImpl : public Renderer {
|
||||||
|
public:
|
||||||
|
explicit SDLRendererImpl(SDL_Renderer* rdr) : rdr_(rdr) {}
|
||||||
|
~SDLRendererImpl() override = default;
|
||||||
|
|
||||||
|
SDL_Texture* createTextureFromSurface(SDL_Surface* surf) override {
|
||||||
|
if (!rdr_ || !surf) return nullptr;
|
||||||
|
return SDL_CreateTextureFromSurface(rdr_, surf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyTexture(SDL_Texture* tex) override {
|
||||||
|
if (tex) SDL_DestroyTexture(tex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy(SDL_Texture* tex, const SDL_Rect* src, const SDL_Rect* dst) override {
|
||||||
|
if (!rdr_ || !tex) return;
|
||||||
|
// SDL_RenderCopy mapping differs across SDL versions; defer to existing renderers
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "SDLRenderer::copy called — fallback no-op (use RenderManager for real draws)");
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear(const SDL_Color& color) override {
|
||||||
|
if (!rdr_) return;
|
||||||
|
SDL_SetRenderDrawColor(rdr_, color.r, color.g, color.b, color.a);
|
||||||
|
SDL_RenderClear(rdr_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void present() override {
|
||||||
|
if (!rdr_) return;
|
||||||
|
SDL_RenderPresent(rdr_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SDL_Renderer* rdr_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Factory helper
|
||||||
|
std::unique_ptr<Renderer> MakeSDLRenderer(SDL_Renderer* rdr) {
|
||||||
|
return std::make_unique<SDLRendererImpl>(rdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace renderer
|
||||||
41
src/resources/ResourceManager.cpp
Normal file
41
src/resources/ResourceManager.cpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include "ResourceManager.h"
|
||||||
|
#include <future>
|
||||||
|
|
||||||
|
namespace resources {
|
||||||
|
|
||||||
|
ResourceManager::ResourceManager() = default;
|
||||||
|
ResourceManager::~ResourceManager() = default;
|
||||||
|
|
||||||
|
std::future<std::shared_ptr<void>> ResourceManager::loadAsync(const std::string& key, std::function<std::shared_ptr<void>(const std::string&)> loader)
|
||||||
|
{
|
||||||
|
// Quick check for existing cached resource
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(mutex_);
|
||||||
|
auto it = cache_.find(key);
|
||||||
|
if (it != cache_.end()) {
|
||||||
|
// Return already-available resource (keep strong ref)
|
||||||
|
auto sp = it->second;
|
||||||
|
if (sp) {
|
||||||
|
return std::async(std::launch::deferred, [sp]() { return sp; });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Launch async loader
|
||||||
|
return std::async(std::launch::async, [this, key, loader]() {
|
||||||
|
auto res = loader(key);
|
||||||
|
if (res) {
|
||||||
|
std::lock_guard<std::mutex> lk(mutex_);
|
||||||
|
cache_[key] = res; // store strong reference
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceManager::put(const std::string& key, std::shared_ptr<void> resource)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(mutex_);
|
||||||
|
cache_[key] = resource; // store strong reference so callers using raw pointers stay valid
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace resources
|
||||||
43
src/resources/ResourceManager.h
Normal file
43
src/resources/ResourceManager.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <mutex>
|
||||||
|
#include <future>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace resources {
|
||||||
|
|
||||||
|
class ResourceManager {
|
||||||
|
public:
|
||||||
|
ResourceManager();
|
||||||
|
~ResourceManager();
|
||||||
|
|
||||||
|
// Return cached resource if available and of the right type
|
||||||
|
template<typename T>
|
||||||
|
std::shared_ptr<T> get(const std::string& key)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(mutex_);
|
||||||
|
auto it = cache_.find(key);
|
||||||
|
if (it == cache_.end()) return nullptr;
|
||||||
|
auto sp = it->second;
|
||||||
|
if (!sp) { cache_.erase(it); return nullptr; }
|
||||||
|
return std::static_pointer_cast<T>(sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asynchronously load a resource using the provided loader function.
|
||||||
|
// The loader must return a shared_ptr to the concrete resource (boxed as void).
|
||||||
|
std::future<std::shared_ptr<void>> loadAsync(const std::string& key, std::function<std::shared_ptr<void>(const std::string&)> loader);
|
||||||
|
|
||||||
|
// Insert a resource into the cache (thread-safe)
|
||||||
|
void put(const std::string& key, std::shared_ptr<void> resource);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Keep strong ownership of cached resources so they remain valid
|
||||||
|
// while present in the cache.
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<void>> cache_;
|
||||||
|
std::mutex mutex_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace resources
|
||||||
Reference in New Issue
Block a user