Rendering Engine 0.2.9
Modular Graphics Rendering Engine | v0.2.9
texture_cache.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Alexander Obzherin � Licensed under the zlib License. See LICENSE.md.
2
3#include "texture_cache.hpp"
4#include "image_data_gpu.hpp"
5#include "image_data.hpp"
6#include "i_renderer.hpp"
7#include <string>
8#include <vector>
9#include "boost/filesystem.hpp"
10#include "utility.hpp"
11#include "logger.hpp"
12
13namespace rendering_engine
14{
15
17 :
18 mRenderer(renderer),
19 mTotalSizeRAM(0),
20 mTotalSizeGPU(0)
21{
22 LOG_INFO("TextureCache created.");
24}
25
27{
28 LOG_DEBUG("TextureCache destroyed.");
30}
31
32void TextureCache::LoadTexturesFromFolder(std::string pathToFolder)
33{
34 LOG_INFO("Loading textures from folder: " + pathToFolder);
35 auto start = std::chrono::steady_clock::now();
36 // 1. Check if path is valid and exist
37 boost::filesystem::path pathToDirectory = boost::filesystem::path(pathToFolder);
38 const bool isValidFolderPath = boost::filesystem::exists(boost::filesystem::path(pathToFolder)) && boost::filesystem::is_directory(boost::filesystem::path(pathToFolder));
39 if (!isValidFolderPath)
40 {
41 return;
42 }
43 // 2. Iterate through files in the folder.
44 // if file is in the list of supported extensions
45 for (boost::filesystem::directory_entry& x : boost::filesystem::directory_iterator(pathToDirectory))
46 {
47 auto textureName = UploadTextureToRAM(x.path().string());
48 if (!textureName.empty())
49 {
50 UploadTextureToGPU(textureName);
51 }
52 }
53
54 auto end = std::chrono::steady_clock::now();
55 float ms = std::chrono::duration<float, std::milli>(end - start).count();
56
57 LOG_INFO("Loaded " + std::to_string(mTextures.size()) +
58 " textures from folder in " +
59 std::to_string(ms) + " ms. RAM usage: " +
60 std::to_string(mTotalSizeRAM) + " bytes.");
61}
62
63void TextureCache::LoadTexture(std::string textureName, ImageData imageData)
64{
65 // Upload to RAM with textureName + binaryFileData
66 auto textureNameUploaded = UploadTextureToRAM(textureName, imageData);
67 if (!textureNameUploaded.empty())
68 {
69 UploadTextureToGPU(textureNameUploaded);
70 }
71}
72
74{
75 LOG_INFO("Loading textures from package.");
76 auto start = std::chrono::steady_clock::now();
77 const auto& entries = Utility::GetPackEntries();
78
79 std::string folderEntry = { "Textures/" };
80 for (auto& entry : entries)
81 {
82 const std::string& virtualPath = entry.first;
83 if (virtualPath.rfind(folderEntry, 0) == 0) // starts with Textures/
84 {
85 std::string textureFileName = virtualPath.substr(folderEntry.size());
86
87 std::vector<uint8_t> binaryFileData = Utility::ReadPackedFile(virtualPath);
88 if (binaryFileData.empty())
89 {
90 LOG_ERROR("[TextureCache] Could not read packed texture: "
91 + virtualPath);
92 continue;
93 }
94
95 // Upload to RAM with textureName + binaryFileData
96 auto textureName = UploadTextureToRAM(textureFileName, binaryFileData);
97 if (!textureName.empty())
98 {
99 UploadTextureToGPU(textureName);
100 }
101 }
102 }
103 auto end = std::chrono::steady_clock::now();
104 float ms = std::chrono::duration<float, std::milli>(end - start).count();
105
106 LOG_INFO("Loaded " + std::to_string(mTextures.size()) +
107 " textures from package in " +
108 std::to_string(ms) + " ms. RAM usage: " +
109 std::to_string(mTotalSizeRAM) + " bytes.");
110}
111
112std::string TextureCache::UploadTextureToRAM(std::string path)
113{
114 auto filePath = boost::filesystem::path(path);
115 if (!boost::filesystem::is_regular_file(filePath))
116 {
117 return std::string{};
118 }
119
120 const std::string ext = filePath.extension().string();
121 const bool isExtensionSupported = (ext == ".jpg") || (ext == ".png");
122 if (!isExtensionSupported)
123 {
124 return std::string{};
125 }
126
127 std::string filename = filePath.stem().string();
128 // If texture is already loaded into RAM yet, do not add again.
129 if (auto search = mTextures.find(filename); search != mTextures.end())
130 {
131 return std::string{};
132 }
133 LOG_DEBUG("Uploading texture to RAM: " + filename);
134 auto start = std::chrono::steady_clock::now();
135 mTextures[filename] = std::make_shared<ImageDataGpu>(path, mRenderer);
136
137 size_t size = mTextures.at(filename)->GetSizeInRAM();
138 mTotalSizeRAM += size;
139
140 auto end = std::chrono::steady_clock::now();
141 float ms = std::chrono::duration<float, std::milli>(end - start).count();
142
143 LOG_DEBUG("Texture loaded to RAM: " + filename +
144 " (" + std::to_string(size) +
145 " bytes, " + std::to_string(ms) + " ms)");
146
147 return filename;
148}
149
150std::string TextureCache::UploadTextureToRAM(std::string textureFileName, std::vector<uint8_t> const& fileBytes)
151{
152 auto textureName = boost::filesystem::path(textureFileName).stem().string();
153 // If texture is already loaded into RAM yet, do not add again.
154 if (auto search = mTextures.find(textureName); search != mTextures.end())
155 {
156 return std::string{};
157 }
158 LOG_DEBUG("Uploading texture to RAM: " + textureName);
159 auto start = std::chrono::steady_clock::now();
160 mTextures[textureName] = std::make_shared<ImageDataGpu>(fileBytes, mRenderer);
161
162 size_t size = mTextures.at(textureName)->GetSizeInRAM();
163 mTotalSizeRAM += size;
164 auto end = std::chrono::steady_clock::now();
165 float ms = std::chrono::duration<float, std::milli>(end - start).count();
166
167 LOG_DEBUG("Texture loaded to RAM: " + textureName +
168 " (" + std::to_string(size) +
169 " bytes, " + std::to_string(ms) + " ms)");
170
171 return textureName;
172}
173
174std::string TextureCache::UploadTextureToRAM(std::string textureName, ImageData imageData)
175{
176 // If texture is already loaded into RAM yet, do not add again.
177 if (auto search = mTextures.find(textureName); search != mTextures.end())
178 {
179 return std::string{};
180 }
181 LOG_DEBUG("Uploading texture to RAM: " + textureName);
182 auto start = std::chrono::steady_clock::now();
183 mTextures[textureName] = std::make_shared<ImageDataGpu>(imageData, mRenderer);
184
185 size_t size = mTextures.at(textureName)->GetSizeInRAM();
186 mTotalSizeRAM += size;
187 auto end = std::chrono::steady_clock::now();
188 float ms = std::chrono::duration<float, std::milli>(end - start).count();
189
190 LOG_DEBUG("Texture loaded to RAM: " + textureName +
191 " (" + std::to_string(size) +
192 " bytes, " + std::to_string(ms) + " ms)");
193
194 return textureName;
195}
196
197void TextureCache::UploadTextureToGPU(std::string textureName)
198{
199 LOG_DEBUG("Uploading texture to GPU: " + textureName);
200 auto start = std::chrono::steady_clock::now();
201 // If texture is not loaded in RAM yet, skip loading to GPU.
202 if (auto search = mTextures.find(textureName); search == mTextures.end())
203 {
204 return;
205 }
206 if (mTextures[textureName]->IsOnGPU())
207 {
208 return;
209 }
210
211 mTextures[textureName]->UploadToGPU();
212 size_t size = mTextures[textureName]->GetSizeInGPU();
213 mTotalSizeGPU += size;
214
215 auto end = std::chrono::steady_clock::now();
216 float ms = std::chrono::duration<float, std::milli>(end - start).count();
217
218 LOG_DEBUG("Texture uploaded to GPU: " + textureName +
219 " (" + std::to_string(size) +
220 " bytes, " + std::to_string(ms) + " ms)");
221}
222
223void TextureCache::ReleaseTextureFromGPU(std::string textureName)
224{
225 LOG_DEBUG("Releasing texture from GPU: " + textureName);
226 if (auto search = mTextures.find(textureName); search == mTextures.end())
227 {
228 return;
229 }
230
231 auto& texture = mTextures[textureName];
232 size_t size = texture->GetSizeInGPU();
233 texture->ReleaseFromGPU();
234
235 mTotalSizeGPU -= size;
236}
237
239{
240 for (auto& texture : mTextures)
241 {
242 texture.second->ReleaseFromGPU();
243 }
244 mTotalSizeGPU = 0;
245}
246
248{
249 LOG_INFO("Releasing all textures. RAM usage before clear: " +
250 std::to_string(mTotalSizeRAM) +
251 ", GPU usage: " +
252 std::to_string(mTotalSizeGPU));
254 mTextures.clear();
255 mTotalSizeRAM = 0;
256 mTotalSizeGPU = 0;
257}
258
259std::shared_ptr<ImageDataGpu> TextureCache::GetTextureResources(std::string filename)
260{
261 auto search = mTextures.find(filename);
262 if (search == mTextures.end())
263 {
264 return nullptr;
265 }
266 return search->second;
267}
268
270{
271 if (auto search = mTextures.find(filename); search == mTextures.end())
272 {
273 return nullptr;
274 }
275
276 return mTextures.at(filename)->GetTextureRenderResources();
277}
278
280{
281 return mTotalSizeRAM;
282}
283
285{
286 return mTotalSizeGPU;
287}
288
290{
292}
293
295{
296 for (auto& texture : mTextures)
297 {
298 texture.second->UploadToGPU();
299 size_t size = texture.second->GetSizeInGPU();
300 mTotalSizeGPU += size;
301 }
302}
303
304}
Defines an abstract interface for rendering backends.
Definition: i_renderer.hpp:29
virtual void RegisterObserver(IRendererObserver *notifier)=0
Registers an observer for rendering events.
virtual void UnregisterObserver(IRendererObserver *notifier)=0
Unregisters a previously registered observer.
Interface for backend-specific GPU texture resource management.
Represents raw 2D image data stored in memory.
Definition: image_data.hpp:80
void OnRenderResourcesRelease() override
Renderer callback: release all GPU resources (used during device loss/reset).
std::unordered_map< std::string, std::shared_ptr< ImageDataGpu > > mTextures
void LoadTexturesFromPackage()
Loads all textures from the packed asset archive.
void ReleaseAll()
Fully clears the texture cache, including both RAM and GPU resources.
void ReleaseTextureFromGPU(std::string textureName)
Releases a texture from GPU memory.
void OnRenderResourcesRebuild() override
Renderer callback: re-upload or recreate all GPU resources (used after device reset/rebuild).
std::shared_ptr< ImageDataGpu > GetTextureResources(std::string filename)
Retrieves the full texture resource wrapper from cache.
void ReleaseAllFromGPU()
Releases all cached textures from GPU memory.
std::string UploadTextureToRAM(std::string path)
Loads a single texture into RAM from the given file path.
ITextureRenderResources * GetTextureRenderResources(std::string filename)
Retrieves only the GPU render resources of a cached texture.
void LoadTexture(std::string textureName, ImageData imageData)
size_t GetSizeInRAM() const
Gets the total size of all currently cached textures in RAM.
void LoadTexturesFromFolder(std::string pathToFolder)
Loads all supported texture files (*.jpg, *.png) from a specified folder into RAM and uploads them to...
TextureCache(IRenderer *renderer)
Constructs the TextureCache with a reference to the renderer.
size_t GetSizeInGPU() const
Gets the total size of all textures currently uploaded to GPU.
void UploadTextureToGPU(std::string textureName)
Uploads a texture (previously loaded into RAM) to GPU.
static const PackEntries & GetPackEntries()
Returns the manifest of packed files.
Definition: utility.cpp:281
static std::vector< uint8_t > ReadPackedFile(const std::string &entryPath)
Reads raw bytes of a file stored inside Pack.bin.
Definition: utility.cpp:322
Engine-wide logging system for runtime diagnostics and performance tracking.
#define LOG_DEBUG(msg)
Definition: logger.hpp:38
#define LOG_ERROR(msg)
Definition: logger.hpp:41
#define LOG_INFO(msg)
Definition: logger.hpp:39