Skip to content

Commit 9a6c5fb

Browse files
authored
Merge pull request #20 from htmlboss/enable-texture-compression
Use texture compression in `ResourceManager::LoadTexture`
2 parents 1c6df4a + 947b943 commit 9a6c5fb

File tree

2 files changed

+139
-34
lines changed

2 files changed

+139
-34
lines changed

MP-APS/ResourceManager.cpp

Lines changed: 138 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@
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+
using ImageBuffer = std::unique_ptr<unsigned char []>;
16+
17+
struct CompressedImageDesc {
18+
GLint width{ -1 };
19+
GLint height{ -1 };
20+
GLint size{ -1 };
21+
GLint format{ -1 };
22+
ImageBuffer data;
23+
};
24+
1325
/***********************************************************************************/
1426
void ResourceManager::ReleaseAllResources() {
1527
// Delete cached meshes
@@ -72,62 +84,155 @@ unsigned int ResourceManager::LoadHDRI(const std::string_view path) const {
7284
}
7385

7486
/***********************************************************************************/
75-
unsigned int ResourceManager::LoadTexture(const std::string_view path, const bool useMipMaps, const bool useUnalignedUnpack) {
87+
auto buildTextureCachePath(const std::filesystem::path& filenameNoExt) {
88+
const std::filesystem::path filename{ filenameNoExt.string() + ".bin" };
89+
return std::filesystem::path( COMPRESSED_TEX_DIR / filename);
90+
}
91+
92+
/***********************************************************************************/
93+
void saveCompressedImageToDisk(const std::filesystem::path& target, const CompressedImageDesc& desc) {
94+
if (!std::filesystem::exists(COMPRESSED_TEX_DIR)) {
95+
if (!std::filesystem::create_directories(COMPRESSED_TEX_DIR)) {
96+
std::cerr << "Failed to create texture cache directory: " << COMPRESSED_TEX_DIR << '\n';
97+
return;
98+
}
99+
}
100+
101+
std::ofstream out(target, std::ios::binary);
102+
if (out) {
103+
out.write((char*)(&desc.size), sizeof(CompressedImageDesc::size));
104+
out.write((char*)(&desc.width), sizeof(CompressedImageDesc::width));
105+
out.write((char*)(&desc.height), sizeof(CompressedImageDesc::height));
106+
out.write((char*)(&desc.format), sizeof(CompressedImageDesc::format));
107+
out.write((char*)(desc.data.get()), desc.size);
108+
}
109+
}
110+
111+
/***********************************************************************************/
112+
std::optional<CompressedImageDesc> loadCompressedImageFromDisk(const std::filesystem::path& target) {
113+
CompressedImageDesc desc;
114+
115+
std::ifstream in(target, std::ios::binary);
116+
if (!in) {
117+
return std::nullopt;
118+
}
119+
120+
in.read(reinterpret_cast<char*>(&desc.size), sizeof(CompressedImageDesc::size));
121+
in.read(reinterpret_cast<char*>(&desc.width), sizeof(CompressedImageDesc::width));
122+
in.read(reinterpret_cast<char*>(&desc.height), sizeof(CompressedImageDesc::height));
123+
in.read(reinterpret_cast<char*>(&desc.format), sizeof(CompressedImageDesc::format));
124+
125+
desc.data = std::make_unique<unsigned char[]>(desc.size);
126+
in.read(reinterpret_cast<char*>(desc.data.get()), desc.size);
127+
128+
return std::make_optional(std::move(desc));
129+
}
130+
131+
/***********************************************************************************/
132+
unsigned int ResourceManager::LoadTexture(const std::filesystem::path& path, const bool useMipMaps, const bool useUnalignedUnpack) {
76133

77-
// Check if texture is already loaded somewhere
78-
const auto val = m_textureCache.find(path.data());
134+
if (path.filename().empty()) {
135+
return 0;
136+
}
79137

80-
if (val != m_textureCache.end()) {
138+
const auto compressedFilePath{ buildTextureCachePath(path.stem()) };
139+
const auto compressedImageExists{ std::filesystem::exists(compressedFilePath) };
140+
141+
const std::filesystem::path pathToLoad = compressedImageExists ? compressedFilePath : path;
142+
143+
// Check if texture is already loaded in memory
144+
if (const auto val = m_textureCache.find(pathToLoad); val != m_textureCache.end()) {
81145
// Found it
82146
return val->second;
83147
}
84-
148+
85149
if (useUnalignedUnpack) {
86150
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
87151
}
88152

89-
// Create and cache a new texture
90153
unsigned int textureID;
91154
glGenTextures(1, &textureID);
92155

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;
156+
if (compressedImageExists) {
157+
158+
const auto desc{ loadCompressedImageFromDisk(compressedFilePath) };
159+
if (!desc) {
160+
return 0;
161+
}
162+
163+
glBindTexture(GL_TEXTURE_2D, textureID);
164+
glCompressedTexImage2D(
165+
GL_TEXTURE_2D,
166+
0,
167+
desc.value().format,
168+
desc.value().width,
169+
desc.value().height,
170+
0,
171+
desc.value().size,
172+
desc.value().data.get()
173+
);
174+
175+
} else {
176+
int width = 0, height = 0, nrComponents = 0;
177+
unsigned char* data = stbi_load(pathToLoad.c_str(), &width, &height, &nrComponents, 0);
178+
if (!data) {
179+
std::cerr << "Failed to load texture: " << path << std::endl;
180+
glDeleteTextures(1, &textureID);
181+
stbi_image_free(data);
182+
return 0;
183+
}
184+
185+
GLenum format = 0;
186+
GLenum internalFormat = 0;
187+
switch (nrComponents) {
188+
case 1:
189+
format = GL_RED;
190+
internalFormat = GL_COMPRESSED_RED;
191+
break;
192+
case 3:
193+
format = GL_RGB;
194+
internalFormat = GL_COMPRESSED_RGB;
195+
break;
196+
case 4:
197+
format = GL_RGBA;
198+
internalFormat = GL_COMPRESSED_RGBA;
199+
break;
200+
}
201+
202+
glBindTexture(GL_TEXTURE_2D, textureID);
203+
204+
glHint(GL_TEXTURE_COMPRESSION_HINT, GL_DONT_CARE);
205+
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, data);
206+
97207
stbi_image_free(data);
98-
return 0;
99-
}
100208

101-
GLenum format = 0;
102-
switch (nrComponents) {
103-
case 1:
104-
format = GL_RED;
105-
break;
106-
case 3:
107-
format = GL_RGB;
108-
break;
109-
case 4:
110-
format = GL_RGBA;
111-
break;
112-
}
209+
GLint compressed = GL_FALSE;
210+
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED, &compressed);
211+
if (compressed == GL_TRUE) {
212+
CompressedImageDesc desc{
213+
.width = width,
214+
.height = height
215+
};
216+
217+
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &desc.size);
218+
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &desc.format);
219+
220+
desc.data = std::make_unique<unsigned char[]>(desc.size);
221+
glGetCompressedTexImage(GL_TEXTURE_2D, 0, (GLvoid*)desc.data.get());
113222

114-
glBindTexture(GL_TEXTURE_2D, textureID);
115-
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
223+
saveCompressedImageToDisk(compressedFilePath, desc);
224+
}
225+
}
226+
116227
if (useMipMaps) {
117228
glGenerateMipmap(GL_TEXTURE_2D);
118229
}
119230

120-
stbi_image_free(data);
121-
122-
#ifdef _DEBUG
123-
std::cout << "Resource Manager: loaded texture: " << path << std::endl;
124-
#endif
125-
126231
if (useUnalignedUnpack) {
127232
glPixelStorei(GL_UNPACK_ALIGNMENT, 0);
128233
}
129234

130-
return m_textureCache.try_emplace(path.data(), textureID).first->second;
235+
return m_textureCache.try_emplace(path, textureID).first->second;
131236
}
132237

133238
/***********************************************************************************/

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)