Skip to content

Commit 4eddbc9

Browse files
committed
Save compressed images to disk
Use compressed images during subsequent loads
1 parent c8a34d0 commit 4eddbc9

File tree

2 files changed

+132
-39
lines changed

2 files changed

+132
-39
lines changed

MP-APS/ResourceManager.cpp

Lines changed: 131 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@
1010
#define STBI_FAILURE_USERMSG
1111
#include <stb_image.h>
1212

13+
const static std::filesystem::path COMPRESSED_TEX_DIR{ std::filesystem::current_path() / "Data/cache/textures" };
14+
15+
struct CompressedImageDesc {
16+
GLint width{ -1 };
17+
GLint height{ -1 };
18+
GLint size{ -1 };
19+
GLint format{ -1 };
20+
unsigned char* data{ nullptr };
21+
};
22+
1323
/***********************************************************************************/
1424
void ResourceManager::ReleaseAllResources() {
1525
// Delete cached meshes
@@ -72,66 +82,149 @@ unsigned int ResourceManager::LoadHDRI(const std::string_view path) const {
7282
}
7383

7484
/***********************************************************************************/
75-
unsigned int ResourceManager::LoadTexture(const std::string_view path, const bool useMipMaps, const bool useUnalignedUnpack) {
85+
auto buildTextureCachePath(const std::filesystem::path& filenameNoExt) {
86+
const std::filesystem::path filename{ filenameNoExt.string() + ".bin" };
87+
return std::filesystem::path( COMPRESSED_TEX_DIR / filename);
88+
}
89+
90+
/***********************************************************************************/
91+
void saveCompressedImageToDisk(const std::filesystem::path& target, const CompressedImageDesc& desc) {
92+
if (!std::filesystem::exists(COMPRESSED_TEX_DIR)) {
93+
if (!std::filesystem::create_directories(COMPRESSED_TEX_DIR)) {
94+
std::cerr << "Failed to create texture cache directory: " << COMPRESSED_TEX_DIR << '\n';
95+
return;
96+
}
97+
}
98+
99+
std::ofstream out(target, std::ios::binary);
100+
if (out) {
101+
out.write((char*)(&desc.size), sizeof(GLint));
102+
out.write((char*)(&desc.width), sizeof(GLint));
103+
out.write((char*)(&desc.height), sizeof(GLint));
104+
out.write((char*)(&desc.format), sizeof(GLint));
105+
out.write((char*)(desc.data), desc.size);
106+
}
107+
}
108+
109+
/***********************************************************************************/
110+
unsigned int ResourceManager::LoadTexture(const std::filesystem::path& path, const bool useMipMaps, const bool useUnalignedUnpack) {
76111

77-
// Check if texture is already loaded somewhere
78-
const auto val = m_textureCache.find(path.data());
112+
if (path.filename().empty()) {
113+
return 0;
114+
}
79115

80-
if (val != m_textureCache.end()) {
116+
const auto compressedFilePath{ buildTextureCachePath(path.stem()) };
117+
const auto compressedImageExists{ std::filesystem::exists(compressedFilePath) };
118+
119+
const std::filesystem::path pathToLoad = compressedImageExists ? compressedFilePath : path;
120+
121+
// Check if texture is already loaded in memory
122+
if (const auto val = m_textureCache.find(pathToLoad); val != m_textureCache.end()) {
81123
// Found it
82124
return val->second;
83125
}
84-
126+
85127
if (useUnalignedUnpack) {
86128
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
87129
}
88130

89-
// Create and cache a new texture
90131
unsigned int textureID;
91132
glGenTextures(1, &textureID);
92133

93-
int width, height, nrComponents;
94-
unsigned char* data = stbi_load(path.data(), &width, &height, &nrComponents, 0);
95-
if (!data) {
96-
std::cerr << "Failed to load texture: " << path << std::endl;
134+
if (compressedImageExists) {
135+
std::ifstream in(compressedFilePath, std::ios::binary);
136+
in.exceptions(std::ifstream::failbit | std::ifstream::badbit);
137+
if (!in) {
138+
return 0;
139+
}
140+
141+
GLint size{ -1 };
142+
GLint width{ -1 };
143+
GLint height{ -1 };
144+
GLint format{ -1 };
145+
146+
in.read((char*)&size, sizeof(GLint));
147+
if (in.fail() || size == -1) {
148+
return 0;
149+
}
150+
151+
in.read((char*)&width, sizeof(GLint));
152+
in.read((char*)&height, sizeof(GLint));
153+
in.read((char*)&format, sizeof(GLint));
154+
155+
auto compressedData = std::make_unique<unsigned char[]>(size);
156+
in.read((char*)compressedData.get(), size);
157+
158+
glBindTexture(GL_TEXTURE_2D, textureID);
159+
glCompressedTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, size, compressedData.get());
160+
161+
} else {
162+
int width = 0, height = 0, nrComponents = 0;
163+
unsigned char* data = stbi_load(pathToLoad.c_str(), &width, &height, &nrComponents, 0);
164+
if (!data) {
165+
std::cerr << "Failed to load texture: " << path << std::endl;
166+
glDeleteTextures(1, &textureID);
167+
stbi_image_free(data);
168+
return 0;
169+
}
170+
171+
GLenum format = 0;
172+
GLenum internalFormat = 0;
173+
switch (nrComponents) {
174+
case 1:
175+
format = GL_RED;
176+
internalFormat = GL_COMPRESSED_RED;
177+
break;
178+
case 3:
179+
format = GL_RGB;
180+
internalFormat = GL_COMPRESSED_RGB;
181+
break;
182+
case 4:
183+
format = GL_RGBA;
184+
internalFormat = GL_COMPRESSED_RGBA;
185+
break;
186+
}
187+
188+
glBindTexture(GL_TEXTURE_2D, textureID);
189+
190+
glHint(GL_TEXTURE_COMPRESSION_HINT, GL_DONT_CARE);
191+
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, data);
192+
97193
stbi_image_free(data);
98-
return 0;
99-
}
100194

101-
GLenum format = 0;
102-
GLenum internalFormat = 0;
103-
switch (nrComponents) {
104-
case 1:
105-
format = GL_RED;
106-
internalFormat = GL_COMPRESSED_RED;
107-
break;
108-
case 3:
109-
format = GL_RGB;
110-
internalFormat = GL_COMPRESSED_RGB;
111-
break;
112-
case 4:
113-
format = GL_RGBA;
114-
internalFormat = GL_COMPRESSED_RGBA;
115-
break;
116-
}
117-
118-
glBindTexture(GL_TEXTURE_2D, textureID);
119-
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, data);
195+
GLint compressed = GL_FALSE;
196+
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED, &compressed);
197+
if (compressed == GL_TRUE) {
198+
GLint compressedSize = -1;
199+
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &compressedSize);
200+
201+
GLint internalFormat = -1;
202+
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat);
203+
204+
auto compressedData = std::make_unique<unsigned char[]>(compressedSize);
205+
glGetCompressedTexImage(GL_TEXTURE_2D, 0, (GLvoid*)compressedData.get());
206+
207+
const CompressedImageDesc desc {
208+
.width = width,
209+
.height = height,
210+
.size = compressedSize,
211+
.format = internalFormat,
212+
.data = compressedData.get()
213+
};
214+
215+
saveCompressedImageToDisk(compressedFilePath, desc);
216+
}
217+
}
218+
120219
if (useMipMaps) {
121220
glGenerateMipmap(GL_TEXTURE_2D);
122221
}
123222

124-
stbi_image_free(data);
125-
126-
#ifdef _DEBUG
127-
std::cout << "Resource Manager: loaded texture: " << path << std::endl;
128-
#endif
129-
130223
if (useUnalignedUnpack) {
131224
glPixelStorei(GL_UNPACK_ALIGNMENT, 0);
132225
}
133226

134-
return m_textureCache.try_emplace(path.data(), textureID).first->second;
227+
return m_textureCache.try_emplace(path, textureID).first->second;
135228
}
136229

137230
/***********************************************************************************/

MP-APS/ResourceManager.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class ResourceManager {
2626
// Loads an HDR image and generates an OpenGL floating-point texture.
2727
unsigned int LoadHDRI(const std::string_view path) const;
2828
// Loads an image (if not cached) and generates an OpenGL texture.
29-
unsigned int LoadTexture(const std::string_view path, const bool useMipMaps = true, const bool useUnalignedUnpack = false);
29+
unsigned int LoadTexture(const std::filesystem::path& path, const bool useMipMaps = true, const bool useUnalignedUnpack = false);
3030
// Loads a binary file into a vector and returns it
3131
std::vector<char> LoadBinaryFile(const std::string_view path) const;
3232

0 commit comments

Comments
 (0)