Flint Engine
Modular C++20 2D game engine powered by SDL3
Loading...
Searching...
No Matches
assets-manager.h
1// SPDX-License-Identifier: MIT
2// SPDX-FileCopyrightText: 2025-2026 Manuel Osorio
3#pragma once
4
5#include "flint/core/asset-loader.h"
6#include "flint/graphics/gpu/gpu-texture.h"
7#include "flint/time/time.h"
8
9#include <cstdint>
10#include <functional>
11#include <memory>
12#include <string>
13#include <unordered_map>
14#include <utility>
15
16namespace Flint {
17
38 struct ManagerLifetime {};
39
40 AssetManager(const AssetManager &) = delete;
41 AssetManager &operator=(const AssetManager &) = delete;
42
43 public:
52 AssetManager(IAssetLoader &loader, SDL_GPUDevice *gpuDevice, bool hotReloadEnable)
53 : m_loader(loader),
54 m_gpuDevice(gpuDevice),
55 m_hotReloadEnabled(hotReloadEnable) {
56 m_hotReloadClock.start();
57 }
58
67 m_managerLifetime.reset();
68 }
69
75 using ReloadCallback = std::function<void(const std::string &)>;
76
95 public:
97 WatchHandle() = default;
98
106 WatchHandle(AssetManager *manager, uint64_t id,
107 std::weak_ptr<ManagerLifetime> managerLifetime)
108 : m_manager(manager),
109 m_id(id),
110 m_managerLifetime(std::move(managerLifetime)) {
111 }
112
115 reset();
116 }
117
119 WatchHandle(WatchHandle &&other) noexcept
120 : m_manager(other.m_manager),
121 m_id(other.m_id),
122 m_managerLifetime(std::move(other.m_managerLifetime)) {
123 other.m_manager = nullptr;
124 }
125
132 WatchHandle &operator=(WatchHandle &&other) noexcept {
133 if (this != &other) {
134 reset();
135
136 m_manager = other.m_manager;
137 m_id = other.m_id;
138 m_managerLifetime = std::move(other.m_managerLifetime);
139
140 other.m_manager = nullptr;
141 }
142 return *this;
143 }
144
145 WatchHandle(const WatchHandle &) = delete;
146 WatchHandle &operator=(const WatchHandle &) = delete;
147
156 [[nodiscard]] bool isActive() const {
157 return m_manager != nullptr && !m_managerLifetime.expired();
158 }
159
165 void reset() {
166 if (m_manager && !m_managerLifetime.expired()) {
167 m_manager->unwatch(m_id);
168 }
169
170 m_manager = nullptr;
171 m_managerLifetime.reset();
172 }
173
174 private:
175 AssetManager *m_manager = nullptr;
176 uint64_t m_id = 0;
177
178 std::weak_ptr<ManagerLifetime> m_managerLifetime;
179 };
180
196 GPUTexture &getTexture(const std::string &path);
197
218 [[nodiscard]] WatchHandle watch(const std::string &path, ReloadCallback callback);
219
233 void update();
234
243 return m_loader;
244 }
245
255 void setHotReloadEnabled(bool enabled) {
256 m_hotReloadEnabled = enabled;
257 }
258
264 [[nodiscard]] bool isHotReloadEnabled() const {
265 return m_hotReloadEnabled;
266 }
267
268 private:
269 std::shared_ptr<ManagerLifetime> m_managerLifetime = std::make_shared<ManagerLifetime>();
270
272 void unwatch(uint64_t id);
273
275 struct CachedTexture {
276 GPUTexture texture;
277 uint64_t revision = 0;
278 bool missing = false;
279 };
280
282 struct WatchedAsset {
283 std::string path;
284 uint64_t revision = 0;
285 uint64_t pendingRevision = 0;
286 uint64_t pendingSince = 0;
287 ReloadCallback callback;
288 bool missing = false;
289 };
290
291 IAssetLoader &m_loader;
292 SDL_GPUDevice *m_gpuDevice = nullptr;
293
294 bool m_hotReloadEnabled = false;
295 Clock m_hotReloadClock;
297 static constexpr uint64_t HOT_RELOAD_INTERVAL = 250;
299 static constexpr uint64_t PENDING_RELOAD_INTERVAL = 150;
300
301 std::unordered_map<std::string, CachedTexture> m_textures;
302 std::unordered_map<uint64_t, WatchedAsset> m_watched;
303 uint64_t m_nextWatchId = 1;
304 };
305}
bool isActive() const
Return whether this handle is still registered.
WatchHandle(WatchHandle &&other) noexcept
Move constructor; transfers ownership and nulls the source.
void reset()
Cancel the watch and release the handle.
WatchHandle(AssetManager *manager, uint64_t id, std::weak_ptr< ManagerLifetime > managerLifetime)
Construct an active handle tied to a specific watch entry.
WatchHandle & operator=(WatchHandle &&other) noexcept
Move-assignment operator.
~WatchHandle()
Destructor; calls reset() to cancel the watch if still active.
RAII handle returned by watch().
WatchHandle()=default
Construct an empty (inactive) handle.
AssetManager(IAssetLoader &loader, SDL_GPUDevice *gpuDevice, bool hotReloadEnable)
Construct an AssetManager.
IAssetLoader & getLoader()
Return a reference to the underlying asset loader.
WatchHandle watch(const std::string &path, ReloadCallback callback)
Register a callback to be invoked whenever path is reloaded.
~AssetManager()
Destroy the AssetManager.
std::function< void(const std::string &)> ReloadCallback
Callback type invoked when a watched asset is reloaded.
void update()
Poll for hot-reload changes and fire pending callbacks.
bool isHotReloadEnabled() const
Return whether hot-reload polling is currently active.
GPUTexture & getTexture(const std::string &path)
Return a GPU texture for path, uploading it on first access.
void setHotReloadEnabled(bool enabled)
Enable or disable hot-reload polling.
Central resource cache and hot-reload coordinator.
void start()
Start the clock if it is currently stopped.
Owns an SDL GPU texture and its associated sampler.
Definition gpu-texture.h:20
Abstract interface for loading raw asset data by path.