Extend caching abilities to compressed textures. Also, improve the structure of cache directories to fit new needs.

This commit is contained in:
Guillaume P 2014-05-08 18:04:22 +02:00
parent ab824f0ac3
commit af91b8b4ae
6 changed files with 160 additions and 18 deletions

View File

@ -68,6 +68,8 @@ PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer;
PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex;
PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding;
PFNGLBLENDCOLORPROC glBlendColor;
PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D;
PFNGLGETCOMPRESSEDTEXIMAGEPROC glGetCompressedTexImage;
#endif
static bool is_gl_init = false;
@ -215,6 +217,8 @@ void initGL()
glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)IRR_OGL_LOAD_EXTENSION("glGetUniformBlockIndex");
glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)IRR_OGL_LOAD_EXTENSION("glUniformBlockBinding");
glBlendColor = (PFNGLBLENDCOLORPROC)IRR_OGL_LOAD_EXTENSION("glBlendColor");
glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)IRR_OGL_LOAD_EXTENSION("glCompressedTexImage2D");
glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)IRR_OGL_LOAD_EXTENSION("glGetCompressedTexImage");
#ifdef DEBUG
glDebugMessageCallbackARB = (PFNGLDEBUGMESSAGECALLBACKARBPROC)IRR_OGL_LOAD_EXTENSION("glDebugMessageCallbackARB");
#endif
@ -303,17 +307,32 @@ GLuint getDepthTexture(irr::video::ITexture *tex)
}
std::set<irr::video::ITexture *> AlreadyTransformedTexture;
void compressTexture(irr::video::ITexture *tex, bool srgb)
{
if (AlreadyTransformedTexture.find(tex) != AlreadyTransformedTexture.end())
return;
AlreadyTransformedTexture.insert(tex);
glBindTexture(GL_TEXTURE_2D, getTextureGLuint(tex));
std::string cached_file;
if (UserConfigParams::m_texture_compression)
{
// Try to retrieve the compressed texture in cache
std::string tex_name = irr_driver->getTextureName(tex);
if (!tex_name.empty()) {
cached_file = file_manager->getTextureCacheLocation(tex_name) + ".gltz";
if (!file_manager->fileIsNewer(tex_name, cached_file)) {
if (loadCompressedTexture(cached_file))
return;
}
}
}
size_t w = tex->getSize().Width, h = tex->getSize().Height;
char *data = new char[w * h * 4];
memcpy(data, tex->lock(), w * h * 4);
tex->unlock();
glBindTexture(GL_TEXTURE_2D, getTextureGLuint(tex));
unsigned internalFormat, Format;
if (tex->hasAlpha())
Format = GL_BGRA;
@ -337,6 +356,84 @@ void compressTexture(irr::video::ITexture *tex, bool srgb)
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, Format, GL_UNSIGNED_BYTE, (GLvoid *)data);
glGenerateMipmap(GL_TEXTURE_2D);
delete[] data;
if (UserConfigParams::m_texture_compression && !cached_file.empty())
{
// Save the compressed texture in the cache for later use.
saveCompressedTexture(cached_file);
}
}
//-----------------------------------------------------------------------------
/** Try to load a compressed texture from the given file name.
* Data in the specified file need to have a specific format. See the
* saveCompressedTexture() function for a description of the format.
* \return true if the loading succeeded, false otherwise.
* \see saveCompressedTexture
*/
bool loadCompressedTexture(const std::string& compressed_tex)
{
std::ifstream ifs(compressed_tex, std::ios::in | std::ios::binary);
if (!ifs.is_open())
return false;
int internal_format;
int w, h;
int size = -1;
ifs.read((char*)&internal_format, sizeof(int));
ifs.read((char*)&w, sizeof(int));
ifs.read((char*)&h, sizeof(int));
ifs.read((char*)&size, sizeof(int));
if (ifs.fail() || size == -1)
return false;
char *data = new char[size];
ifs.read(data, size);
if (!ifs.fail())
{
glCompressedTexImage2D(GL_TEXTURE_2D, 0, internal_format,
w, h, 0, size, (GLvoid*)data);
glGenerateMipmap(GL_TEXTURE_2D);
delete[] data;
ifs.close();
return true;
}
delete[] data;
return false;
}
//-----------------------------------------------------------------------------
/** Try to save the last texture sent to glTexImage2D in a file of the given
* file name. This function should only be used for textures sent to
* glTexImage2D with a compressed internal format as argument.<br>
* \note The following format is used to save the compressed texture:<br>
* <internal-format><width><height><size><data> <br>
* The first four elements are integers and the last one is stored
* on \c size bytes.
* \see loadCompressedTexture
*/
void saveCompressedTexture(const std::string& compressed_tex)
{
int internal_format, width, height, size;
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, (GLint *)&internal_format);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, (GLint *)&width);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, (GLint *)&height);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, (GLint *)&size);
char *data = new char[size];
glGetCompressedTexImage(GL_TEXTURE_2D, 0, (GLvoid*)data);
std::ofstream ofs(compressed_tex, std::ios::out | std::ios::binary);
if (ofs.is_open())
{
ofs.write((char*)&internal_format, sizeof(int));
ofs.write((char*)&width, sizeof(int));
ofs.write((char*)&height, sizeof(int));
ofs.write((char*)&size, sizeof(int));
ofs.write(data, size);
ofs.close();
}
delete[] data;
}
void setTexture(unsigned TextureUnit, GLuint TextureId, GLenum MagFilter, GLenum MinFilter, bool allowAF)

View File

@ -85,6 +85,8 @@ extern PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer;
extern PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex;
extern PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding;
extern PFNGLBLENDCOLORPROC glBlendColor;
extern PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D;
extern PFNGLGETCOMPRESSEDTEXIMAGEPROC glGetCompressedTexImage;
#ifdef DEBUG
extern PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARB;
#endif
@ -160,6 +162,8 @@ GLint LoadProgram(Types ... args)
GLuint getTextureGLuint(irr::video::ITexture *tex);
GLuint getDepthTexture(irr::video::ITexture *tex);
void compressTexture(irr::video::ITexture *tex, bool srgb);
bool loadCompressedTexture(const std::string& compressed_tex);
void saveCompressedTexture(const std::string& compressed_tex);
void blitFBO(GLuint Src, GLuint Dst, size_t width, size_t height);
void draw2DImage(const irr::video::ITexture* texture, const irr::core::rect<s32>& destRect,

View File

@ -1245,7 +1245,8 @@ void IrrDriver::unsetTextureErrorMessage()
* version for each of them and save them in the cache. Smaller textures are
* generated only if they do not already exist or if their original version
* is newer than the cached one.
* \param dir Directory from where textures will be retrieve.
* \param dir Directory from where textures will be retrieved.
* Must end with '/'.
* \return Directory where smaller textures were cached.
*/
std::string IrrDriver::generateSmallerTextures(const std::string& dir)
@ -1263,7 +1264,7 @@ std::string IrrDriver::generateSmallerTextures(const std::string& dir)
}
} // for it in files
return file_manager->getCachedTexturesDir();
return file_manager->getTextureCacheLocation(dir);
} // generateSmallerTextures
// ----------------------------------------------------------------------------
@ -1272,21 +1273,11 @@ std::string IrrDriver::generateSmallerTextures(const std::string& dir)
* exist or if the original version is newer than the cached one.
* \param filename File name of the original texture.
* \return File name of the cached texture.
*
* \todo Regenerate the texture if the original version is newer than the
* cached one.
*/
std::string IrrDriver::getSmallerTexture(const std::string& filename)
{
// Retrieve the filename of the cached texture
std::string file = StringUtils::getBasename(filename);
std::string parent_dir = StringUtils::getPath(filename);
if (StringUtils::hasSuffix(parent_dir, "/"))
parent_dir = parent_dir.substr(0, parent_dir.size() - 1);
parent_dir = StringUtils::getBasename(parent_dir);
std::string cached_file = file_manager->getCachedTexturesDir() + parent_dir + "/";
file_manager->checkAndCreateDirectoryP(cached_file);
cached_file += file;
std::string cached_file = file_manager->getTextureCacheLocation(filename);
// If the cached texture does not exist, we generate it.
if (!file_manager->fileExists(cached_file) ||
@ -1428,9 +1419,26 @@ video::ITexture *IrrDriver::getTexture(const std::string &filename,
Log::error("irr_driver", "Texture '%s' not found.", filename.c_str());
}
m_texturesFileName[out] = filename;
return out;
} // getTexture
// ----------------------------------------------------------------------------
/** Get the texture file name using a texture pointer.
* \param tex Pointer on the texture for which we want to find the file name.
* \return Filename of the texture if found, or an empty string otherwise.
*/
std::string IrrDriver::getTextureName(video::ITexture* tex)
{
std::map<video::ITexture*, std::string>::iterator it;
it = m_texturesFileName.find(tex);
if (it != m_texturesFileName.end())
return it->second;
else
return "";
} // getTextureName
// ----------------------------------------------------------------------------
/** Appends a pointer to each texture used in this mesh to the vector.
* \param mesh The mesh from which the textures are being determined.

View File

@ -133,6 +133,8 @@ private:
float greenSHCoeff[9];
float redSHCoeff[9];
/** Keep a trace of the origin file name of a texture. */
std::map<video::ITexture*, std::string> m_texturesFileName;
/** Flag to indicate if a resolution change is pending (which will be
* acted upon in the next update). None means no change, yes means
@ -311,6 +313,7 @@ public:
bool is_premul=false,
bool is_prediv=false,
bool complain_if_not_found=true);
std::string getTextureName(video::ITexture* tex);
void grabAllTextures(const scene::IMesh *mesh);
void dropAllTextures(const scene::IMesh *mesh);
scene::IMesh *createQuadMesh(const video::SMaterial *material=NULL,

View File

@ -868,13 +868,13 @@ void FileManager::checkAndCreateScreenshotDir()
void FileManager::checkAndCreateCachedTexturesDir()
{
#if defined(WIN32) || defined(__CYGWIN__)
m_cached_textures_dir = m_user_config_dir + "resized-textures/";
m_cached_textures_dir = m_user_config_dir + "cached-textures/";
#elif defined(__APPLE__)
m_cached_textures_dir = getenv("HOME");
m_cached_textures_dir += "/Library/Application Support/SuperTuxKart/ResizedTextures/";
m_cached_textures_dir += "/Library/Application Support/SuperTuxKart/CachedTextures/";
#else
m_cached_textures_dir = checkAndCreateLinuxDir("XDG_CACHE_HOME", "supertuxkart", ".cache/", ".");
m_cached_textures_dir += "resized-textures/";
m_cached_textures_dir += "cached-textures/";
#endif
if (!checkAndCreateDirectory(m_cached_textures_dir))
@ -1010,6 +1010,35 @@ void FileManager::redirectOutput()
Log::openOutputFiles(logoutfile);
} // redirectOutput
//-----------------------------------------------------------------------------
/** Returns the theoretical location of the cached version of a texture
* depending of the current config. (This function also works for directories:
* in this case the returned directory will be the cache location for all
* textures that you will find in the specified directory. The specified
* directory must end with '/')
* \note The returned location is where the cached data should be read or
* written but the file itseft does not necessarity exist. However, the
* directory structure is automatically created if it does not exist.
*/
std::string FileManager::getTextureCacheLocation(const std::string& filename)
{
std::string file = StringUtils::getBasename(filename);
std::string parent_dir = StringUtils::getPath(filename);
if (StringUtils::hasSuffix(parent_dir, "/"))
parent_dir = parent_dir.substr(0, parent_dir.size() - 1);
parent_dir = StringUtils::getBasename(parent_dir);
std::string cache_subdir = UserConfigParams::m_high_definition_textures
? "hd/"
: "resized/";
std::string cached_file =
getCachedTexturesDir() + cache_subdir + parent_dir + "/";
checkAndCreateDirectoryP(cached_file);
cached_file += file;
return cached_file;
} // getTextureCacheLocation
//-----------------------------------------------------------------------------
/** Returns the directory for addon files. */
const std::string &FileManager::getAddonsDir() const

View File

@ -116,6 +116,7 @@ public:
std::string getScreenshotDir() const;
std::string getCachedTexturesDir() const;
std::string getGPDir() const;
std::string getTextureCacheLocation(const std::string& filename);
bool checkAndCreateDirectoryP(const std::string &path);
const std::string &getAddonsDir() const;
std::string getAddonsFile(const std::string &name);