Commit 0480dd96 authored by Jakob Weiss's avatar Jakob Weiss
Browse files

Fixed Texture Manager

TextureManager now uses a configurable lifetime for the lifetime of a cache object. Upon every garbage collection event, the counter is incremented for each object in the cache and if a counter exceeds TextureManager::garbageLifetime, the texture id is marked for deletion.
Deletion now happens in a separate thread, as a job enqueued to the OpenGLJobProcessor, in order to properly acquire a valid opengl context.
parent 29f542ae
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
#include "singleton.h" #include "singleton.h"
#include "gltextureformattraits.h" #include "gltextureformattraits.h"
#include "opengljobprocessor.h"
//#define ENABLE_TEXTURE_POOL
namespace { namespace {
/// private class to format iostream to use points as a thousands separator /// private class to format iostream to use points as a thousands separator
...@@ -54,13 +57,13 @@ void cgt::TextureManager::clearPool() ...@@ -54,13 +57,13 @@ void cgt::TextureManager::clearPool()
std::stringstream ss; std::stringstream ss;
for (auto& it : texturePool_) for (auto& it : texturePool_)
{ {
auto tft = GLTextureFormatTraits::get(std::get<INTERNAL_FORMAT>(it.first)); auto tft = GLTextureFormatTraits::get(it.first.internalFormat);
cgt::svec3 size(std::get<SIZE_X>(it.first), std::get<SIZE_Y>(it.first), std::get<SIZE_Z>(it.first)); cgt::svec3 size(it.first.size_x, it.first.size_y, it.first.size_z);
GLenum textureType = std::get<TEX_TYPE>(it.first); GLenum textureType = it.first.type;
ss << "\t * [" << size.x << "," << size.y << "," << size.z ss << "\t * [" << size.x << "," << size.y << "," << size.z
<< "] " << textureTypeToStr(textureType) << " " << tft.internalFormatName() << std::endl; << "] " << textureTypeToStr(textureType) << " " << tft.internalFormatName() << std::endl;
glDeleteTextures(1, &it.second); glDeleteTextures(1, &it.second.first);
totalBytes += textureByteSize(it.first); totalBytes += textureByteSize(it.first);
texturePoolMemory_ -= textureByteSize(it.first); texturePoolMemory_ -= textureByteSize(it.first);
...@@ -78,29 +81,40 @@ void cgt::TextureManager::performGarbageCollection() ...@@ -78,29 +81,40 @@ void cgt::TextureManager::performGarbageCollection()
dbgOut.imbue(std::locale(std::cout.getloc(), new PointSeparator(1))); dbgOut.imbue(std::locale(std::cout.getloc(), new PointSeparator(1)));
dbgOut << texturePoolMemory_ << " Bytes of in texture pool, additionally " << activeAllocatedMemory_ << " Bytes still in use."; dbgOut << texturePoolMemory_ << " Bytes of in texture pool, additionally " << activeAllocatedMemory_ << " Bytes still in use.";
LDEBUG(dbgOut.str()); dbgOut << "releasing texture pool with " << texturePool_.size() << " textures...";
LDEBUG("releasing texture pool with " << texturePool_.size() << " textures..."); size_t releasedBytes = 0;
size_t totalBytes = 0;
std::stringstream ss; std::stringstream ss;
for (auto& it : texturePool_) std::vector<GLuint> idsToRelease;
for (auto it = texturePool_.begin(); it != texturePool_.end(); )
{ {
auto tft = GLTextureFormatTraits::get(std::get<INTERNAL_FORMAT>(it.first)); auto tft = GLTextureFormatTraits::get(it->first.internalFormat);
cgt::svec3 size(std::get<SIZE_X>(it.first), std::get<SIZE_Y>(it.first), std::get<SIZE_Z>(it.first)); cgt::svec3 size(it->first.size_x, it->first.size_y, it->first.size_z);
GLenum textureType = std::get<TEX_TYPE>(it.first); GLenum textureType = it->first.type;
ss << "\t * [" << size.x << "," << size.y << "," << size.z
<< "] " << textureTypeToStr(textureType) << " " << tft.internalFormatName() << std::endl;
glDeleteTextures(1, &it.second); // Check if pool entry has expired
if (++(it->second.second) >= garbageLifetime) {
ss << "\t * [" << size.x << "," << size.y << "," << size.z
<< "] " << textureTypeToStr(textureType) << " " << tft.internalFormatName() << std::endl;
totalBytes += textureByteSize(it.first); releasedBytes += textureByteSize(it->first);
texturePoolMemory_ -= textureByteSize(it.first); texturePoolMemory_ -= textureByteSize(it->first);
idsToRelease.push_back(it->second.first);
it = texturePool_.erase(it);
}
else
++it;
} }
texturePool_.clear();
std::stringstream ss3; if (!idsToRelease.empty()) {
ss3 << "Released " << totalBytes << " Bytes of texture data:" << std::endl << ss.str(); dbgOut << "Released " << releasedBytes << " Bytes of texture data:" << std::endl << ss.str();
LDEBUG(ss3.str()); std::string debugMessage = dbgOut.str();
GLJobProc.enqueueJob([=] {
glDeleteTextures(static_cast<GLsizei>(idsToRelease.size()), idsToRelease.data());
LDEBUG(debugMessage);
});
}
} }
void cgt::TextureManager::forceGarbageCollection() void cgt::TextureManager::forceGarbageCollection()
...@@ -123,7 +137,7 @@ void cgt::TextureManager::forceClearPool() ...@@ -123,7 +137,7 @@ void cgt::TextureManager::forceClearPool()
GLuint cgt::TextureManager::generateId(GLenum type, const cgt::svec3 & dimensions, GLenum internalFormat) GLuint cgt::TextureManager::generateId(GLenum type, const cgt::svec3 & dimensions, GLenum internalFormat)
{ {
cache_key_t cacheKey(type, dimensions.x, dimensions.y, dimensions.z, internalFormat); cache_key_t cacheKey{ type, dimensions.x, dimensions.y, dimensions.z, internalFormat };
#ifdef ENABLE_TEXTURE_POOL #ifdef ENABLE_TEXTURE_POOL
std::lock_guard<std::mutex> scope_lock(accessMutex_); std::lock_guard<std::mutex> scope_lock(accessMutex_);
...@@ -131,7 +145,7 @@ GLuint cgt::TextureManager::generateId(GLenum type, const cgt::svec3 & dimension ...@@ -131,7 +145,7 @@ GLuint cgt::TextureManager::generateId(GLenum type, const cgt::svec3 & dimension
GLuint id; GLuint id;
auto it = texturePool_.find(cacheKey); auto it = texturePool_.find(cacheKey);
if (it != texturePool_.end()) { if (it != texturePool_.end()) {
id = it->second; id = it->second.first;
texturePool_.erase(it); texturePool_.erase(it);
activeAllocatedMemory_ += textureByteSize(cacheKey); activeAllocatedMemory_ += textureByteSize(cacheKey);
...@@ -198,10 +212,10 @@ GLuint cgt::TextureManager::generateId(GLenum type, const cgt::svec3 & dimension ...@@ -198,10 +212,10 @@ GLuint cgt::TextureManager::generateId(GLenum type, const cgt::svec3 & dimension
void cgt::TextureManager::releaseId(GLuint id, GLenum type, const cgt::svec3 & dimensions, GLenum internalFormat) void cgt::TextureManager::releaseId(GLuint id, GLenum type, const cgt::svec3 & dimensions, GLenum internalFormat)
{ {
cache_key_t cacheKey(type, dimensions.x, dimensions.y, dimensions.z, internalFormat); cache_key_t cacheKey{ type, dimensions.x, dimensions.y, dimensions.z, internalFormat };
#ifdef ENABLE_TEXTURE_POOL #ifdef ENABLE_TEXTURE_POOL
std::lock_guard<std::mutex> scope_lock(accessMutex_); std::lock_guard<std::mutex> scope_lock(accessMutex_);
texturePool_.insert(std::make_pair(cacheKey, id)); texturePool_.insert(std::make_pair(cacheKey, std::make_pair(id, 0)));
activeAllocatedMemory_ -= textureByteSize(cacheKey); activeAllocatedMemory_ -= textureByteSize(cacheKey);
texturePoolMemory_ += textureByteSize(cacheKey); texturePoolMemory_ += textureByteSize(cacheKey);
#else #else
...@@ -225,7 +239,7 @@ void cgt::TextureManager::run() ...@@ -225,7 +239,7 @@ void cgt::TextureManager::run()
constexpr size_t cgt::TextureManager::textureByteSize(const cache_key_t & key) constexpr size_t cgt::TextureManager::textureByteSize(const cache_key_t & key)
{ {
return std::get<SIZE_X>(key) * std::get<SIZE_Y>(key) * std::get<SIZE_Z>(key) * GLTextureFormatTraits::get(std::get<INTERNAL_FORMAT>(key)).bpp(); return key.size_x * key.size_y * key.size_z * GLTextureFormatTraits::get(key.internalFormat).bpp();
} }
std::string cgt::TextureManager::textureTypeToStr(GLenum tt) std::string cgt::TextureManager::textureTypeToStr(GLenum tt)
......
...@@ -38,17 +38,23 @@ namespace cgt { ...@@ -38,17 +38,23 @@ namespace cgt {
private: private:
static const std::string loggerCat_; static const std::string loggerCat_;
using cache_key_t = std::tuple<GLenum, size_t, size_t, size_t, GLenum>;
enum CacheEntryTupleIndex { struct cache_key {
TEX_TYPE = 0, GLenum type;
SIZE_X = 1, size_t size_x, size_y, size_z;
SIZE_Y = 2, GLenum internalFormat;
SIZE_Z = 3, bool operator< (const cache_key& rhs) const {
INTERNAL_FORMAT = 4 return std::make_tuple(type, size_x, size_y, size_z, internalFormat) < std::make_tuple(rhs.type, rhs.size_x, rhs.size_y, rhs.size_z, rhs.internalFormat);
}
}; };
using cache_key_t = cache_key;
/// GC will be performed regularly at this interval, in [ms] /// GC will be performed regularly at this interval, in [ms]
const size_t garbageCollectionDelayMs = 15000; const size_t garbageCollectionDelayMs = 5000;
/// cached objects will be collected after this many GC attempts
const int garbageLifetime = 3;
/// Compute the memory size from the cache entry key /// Compute the memory size from the cache entry key
static constexpr size_t textureByteSize(const cache_key_t& key); static constexpr size_t textureByteSize(const cache_key_t& key);
...@@ -67,7 +73,7 @@ namespace cgt { ...@@ -67,7 +73,7 @@ namespace cgt {
void clearPool(); void clearPool();
/// the actual texture pool containing all textures not in use /// the actual texture pool containing all textures not in use
std::multimap<cache_key_t, GLuint> texturePool_; std::multimap<cache_key_t, std::pair<GLuint, int>> texturePool_;
/// Currently allocated GPU memory from textures still in use /// Currently allocated GPU memory from textures still in use
size_t activeAllocatedMemory_; size_t activeAllocatedMemory_;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment