Notice: If you are member of any public project or group, please make sure that your GitLab username is not the same as the LRZ identifier/Kennung (see https://gitlab.lrz.de/profile/account). Please change your username if necessary. For more information see the section "Public projects / Öffentliche Projekte" at https://doku.lrz.de/display/PUBLIC/GitLab . Thank you!

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

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 @@
#include "singleton.h"
#include "gltextureformattraits.h"
#include "opengljobprocessor.h"
//#define ENABLE_TEXTURE_POOL
namespace {
/// private class to format iostream to use points as a thousands separator
......@@ -54,13 +57,13 @@ void cgt::TextureManager::clearPool()
std::stringstream ss;
for (auto& it : texturePool_)
{
auto tft = GLTextureFormatTraits::get(std::get<INTERNAL_FORMAT>(it.first));
cgt::svec3 size(std::get<SIZE_X>(it.first), std::get<SIZE_Y>(it.first), std::get<SIZE_Z>(it.first));
GLenum textureType = std::get<TEX_TYPE>(it.first);
auto tft = GLTextureFormatTraits::get(it.first.internalFormat);
cgt::svec3 size(it.first.size_x, it.first.size_y, it.first.size_z);
GLenum textureType = it.first.type;
ss << "\t * [" << size.x << "," << size.y << "," << size.z
<< "] " << textureTypeToStr(textureType) << " " << tft.internalFormatName() << std::endl;
glDeleteTextures(1, &it.second);
glDeleteTextures(1, &it.second.first);
totalBytes += textureByteSize(it.first);
texturePoolMemory_ -= textureByteSize(it.first);
......@@ -78,29 +81,40 @@ void cgt::TextureManager::performGarbageCollection()
dbgOut.imbue(std::locale(std::cout.getloc(), new PointSeparator(1)));
dbgOut << texturePoolMemory_ << " Bytes of in texture pool, additionally " << activeAllocatedMemory_ << " Bytes still in use.";
LDEBUG(dbgOut.str());
LDEBUG("releasing texture pool with " << texturePool_.size() << " textures...");
size_t totalBytes = 0;
dbgOut << "releasing texture pool with " << texturePool_.size() << " textures...";
size_t releasedBytes = 0;
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));
cgt::svec3 size(std::get<SIZE_X>(it.first), std::get<SIZE_Y>(it.first), std::get<SIZE_Z>(it.first));
GLenum textureType = std::get<TEX_TYPE>(it.first);
ss << "\t * [" << size.x << "," << size.y << "," << size.z
<< "] " << textureTypeToStr(textureType) << " " << tft.internalFormatName() << std::endl;
auto tft = GLTextureFormatTraits::get(it->first.internalFormat);
cgt::svec3 size(it->first.size_x, it->first.size_y, it->first.size_z);
GLenum textureType = it->first.type;
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);
texturePoolMemory_ -= textureByteSize(it.first);
releasedBytes += textureByteSize(it->first);
texturePoolMemory_ -= textureByteSize(it->first);
idsToRelease.push_back(it->second.first);
it = texturePool_.erase(it);
}
else
++it;
}
texturePool_.clear();
std::stringstream ss3;
ss3 << "Released " << totalBytes << " Bytes of texture data:" << std::endl << ss.str();
LDEBUG(ss3.str());
if (!idsToRelease.empty()) {
dbgOut << "Released " << releasedBytes << " Bytes of texture data:" << std::endl << ss.str();
std::string debugMessage = dbgOut.str();
GLJobProc.enqueueJob([=] {
glDeleteTextures(static_cast<GLsizei>(idsToRelease.size()), idsToRelease.data());
LDEBUG(debugMessage);
});
}
}
void cgt::TextureManager::forceGarbageCollection()
......@@ -123,7 +137,7 @@ void cgt::TextureManager::forceClearPool()
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
std::lock_guard<std::mutex> scope_lock(accessMutex_);
......@@ -131,7 +145,7 @@ GLuint cgt::TextureManager::generateId(GLenum type, const cgt::svec3 & dimension
GLuint id;
auto it = texturePool_.find(cacheKey);
if (it != texturePool_.end()) {
id = it->second;
id = it->second.first;
texturePool_.erase(it);
activeAllocatedMemory_ += textureByteSize(cacheKey);
......@@ -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)
{
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
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);
texturePoolMemory_ += textureByteSize(cacheKey);
#else
......@@ -225,7 +239,7 @@ void cgt::TextureManager::run()
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)
......
......@@ -38,17 +38,23 @@ namespace cgt {
private:
static const std::string loggerCat_;
using cache_key_t = std::tuple<GLenum, size_t, size_t, size_t, GLenum>;
enum CacheEntryTupleIndex {
TEX_TYPE = 0,
SIZE_X = 1,
SIZE_Y = 2,
SIZE_Z = 3,
INTERNAL_FORMAT = 4
struct cache_key {
GLenum type;
size_t size_x, size_y, size_z;
GLenum internalFormat;
bool operator< (const cache_key& rhs) const {
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]
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
static constexpr size_t textureByteSize(const cache_key_t& key);
......@@ -67,7 +73,7 @@ namespace cgt {
void clearPool();
/// 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
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