From ee35c5540e1602f38bf3ae57027fbe3ec8c2702c Mon Sep 17 00:00:00 2001 From: Kearwood Gilbert Date: Mon, 14 Apr 2014 00:47:29 -0700 Subject: [PATCH] Corrected bug that allowed incomplete cube map textures to attempt to load, resulting in gl errors Implemented procedural loading of textures with new streamer algorithm Fixed thread safety issues in streamer (double-buffered old level tracking variables) Texture streamer now only processes once per frame --HG-- branch : nfb --- KREngine/kraken/KRLODSet.cpp | 2 +- KREngine/kraken/KRTexture.cpp | 11 ++++- KREngine/kraken/KRTexture.h | 2 + KREngine/kraken/KRTexture2D.cpp | 16 ++----- KREngine/kraken/KRTexture2D.h | 2 +- KREngine/kraken/KRTextureCube.cpp | 18 +++---- KREngine/kraken/KRTextureCube.h | 2 +- KREngine/kraken/KRTextureKTX.cpp | 6 +-- KREngine/kraken/KRTextureKTX.h | 2 +- KREngine/kraken/KRTextureManager.cpp | 70 ++++++++++++++++++++++------ KREngine/kraken/KRTexturePVR.cpp | 6 +-- KREngine/kraken/KRTexturePVR.h | 2 +- KREngine/kraken/KRTextureTGA.cpp | 4 +- KREngine/kraken/KRTextureTGA.h | 2 +- 14 files changed, 92 insertions(+), 53 deletions(-) diff --git a/KREngine/kraken/KRLODSet.cpp b/KREngine/kraken/KRLODSet.cpp index b855815..d056e30 100644 --- a/KREngine/kraken/KRLODSet.cpp +++ b/KREngine/kraken/KRLODSet.cpp @@ -55,7 +55,7 @@ void KRLODSet::updateLODVisibility(const KRViewport &viewport) } else if(m_activeLODGroup == NULL) { m_activeLODGroup = new_active_lod_group; } else if(new_active_lod_group != m_activeLODGroup) { - if(/*true || */new_active_lod_group->getStreamLevel(true, viewport) >= kraken_stream_level::STREAM_LEVEL_IN_LQ) { // FINDME, HACK! Disabled due to performance issues. + if(true || new_active_lod_group->getStreamLevel(true, viewport) >= kraken_stream_level::STREAM_LEVEL_IN_LQ) { // FINDME, HACK! Disabled due to performance issues. // fprintf(stderr, "LOD %s -> %s\n", m_activeLODGroup->getName().c_str(), new_active_lod_group->getName().c_str()); m_activeLODGroup = new_active_lod_group; } else { diff --git a/KREngine/kraken/KRTexture.cpp b/KREngine/kraken/KRTexture.cpp index 0d9fa1d..5ce80d6 100644 --- a/KREngine/kraken/KRTexture.cpp +++ b/KREngine/kraken/KRTexture.cpp @@ -14,6 +14,8 @@ KRTexture::KRTexture(KRContext &context, std::string name) : KRResource(context, name) { + m_current_lod_max_dim = 0; + m_new_lod_max_dim = 0; m_iHandle = 0; m_iNewHandle = 0; m_textureMemUsed = 0; @@ -45,6 +47,8 @@ void KRTexture::releaseHandles() { m_iHandle = 0; m_textureMemUsed = 0; } + m_current_lod_max_dim = 0; + m_new_lod_max_dim = 0; m_handle_lock.clear(); @@ -71,7 +75,7 @@ void KRTexture::resize(int max_dim) int target_dim = max_dim; if(target_dim < m_min_lod_max_dim) target_dim = m_min_lod_max_dim; - if(m_current_lod_max_dim != target_dim || (m_iHandle == 0 && m_iNewHandle == 0)) { + if(m_new_lod_max_dim != target_dim || (m_iHandle == 0 && m_iNewHandle == 0)) { assert(m_newTextureMemUsed == 0); m_newTextureMemUsed = getMemRequiredForSize(target_dim); @@ -175,6 +179,10 @@ int KRTexture::getCurrentLodMaxDim() { return m_current_lod_max_dim; } +int KRTexture::getNewLodMaxDim() { + return m_new_lod_max_dim; +} + int KRTexture::getMaxMipMap() { return m_max_lod_max_dim; } @@ -207,6 +215,7 @@ void KRTexture::_swapHandles() m_textureMemUsed = (long)m_newTextureMemUsed; m_newTextureMemUsed = 0; m_iHandle = m_iNewHandle; + m_current_lod_max_dim = m_new_lod_max_dim; } m_handle_lock.clear(); } diff --git a/KREngine/kraken/KRTexture.h b/KREngine/kraken/KRTexture.h index cc741fb..fa338b5 100644 --- a/KREngine/kraken/KRTexture.h +++ b/KREngine/kraken/KRTexture.h @@ -80,6 +80,7 @@ public: virtual KRTexture *compress(bool premultiply_alpha = false); int getCurrentLodMaxDim(); + int getNewLodMaxDim(); // For use by streamer only int getMaxMipMap(); int getMinMipMap(); bool hasMipmaps(); @@ -100,6 +101,7 @@ protected: std::atomic_flag m_handle_lock; int m_current_lod_max_dim; + int m_new_lod_max_dim; uint32_t m_max_lod_max_dim; uint32_t m_min_lod_max_dim; diff --git a/KREngine/kraken/KRTexture2D.cpp b/KREngine/kraken/KRTexture2D.cpp index 11918d9..25a7a7e 100644 --- a/KREngine/kraken/KRTexture2D.cpp +++ b/KREngine/kraken/KRTexture2D.cpp @@ -34,7 +34,6 @@ #include "KRTextureManager.h" KRTexture2D::KRTexture2D(KRContext &context, KRDataBlock *data, std::string name) : KRTexture(context, name) { - m_current_lod_max_dim = 0; m_pData = data; } @@ -48,16 +47,11 @@ bool KRTexture2D::createGLTexture(int lod_max_dim) { } bool success = true; - int prev_lod_max_dim = 0; -#if GL_APPLE_copy_texture_levels && GL_EXT_texture_storage - - if(m_iHandle != 0) { - prev_lod_max_dim = m_current_lod_max_dim; - } -#endif + int prev_lod_max_dim = m_new_lod_max_dim; + m_iNewHandle = 0; - m_current_lod_max_dim = 0; + m_new_lod_max_dim = 0; GLDEBUG(glGenTextures(1, &m_iNewHandle)); if(m_iNewHandle == 0) { @@ -71,10 +65,10 @@ bool KRTexture2D::createGLTexture(int lod_max_dim) { GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); } - if(!uploadTexture(GL_TEXTURE_2D, lod_max_dim, m_current_lod_max_dim, prev_lod_max_dim)) { + if(!uploadTexture(GL_TEXTURE_2D, lod_max_dim, m_new_lod_max_dim)) { GLDEBUG(glDeleteTextures(1, &m_iNewHandle)); m_iNewHandle = m_iHandle; - m_current_lod_max_dim = prev_lod_max_dim; + m_new_lod_max_dim = prev_lod_max_dim; success = false; } } diff --git a/KREngine/kraken/KRTexture2D.h b/KREngine/kraken/KRTexture2D.h index 921370b..8b32bdd 100644 --- a/KREngine/kraken/KRTexture2D.h +++ b/KREngine/kraken/KRTexture2D.h @@ -46,7 +46,7 @@ public: virtual bool save(const std::string& path); virtual bool save(KRDataBlock &data); - virtual bool uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, int prev_lod_max_dim, bool compress = false, bool premultiply_alpha = false) = 0; + virtual bool uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, bool compress = false, bool premultiply_alpha = false) = 0; virtual void bind(GLuint texture_unit); protected: diff --git a/KREngine/kraken/KRTextureCube.cpp b/KREngine/kraken/KRTextureCube.cpp index 992fe79..57a7bef 100644 --- a/KREngine/kraken/KRTextureCube.cpp +++ b/KREngine/kraken/KRTextureCube.cpp @@ -46,6 +46,8 @@ KRTextureCube::KRTextureCube(KRContext &context, std::string name) : KRTexture(c if(m_textures[i]) { if(m_textures[i]->getMaxMipMap() < m_max_lod_max_dim) m_max_lod_max_dim = m_textures[i]->getMaxMipMap(); if(m_textures[i]->getMinMipMap() > m_min_lod_max_dim) m_min_lod_max_dim = m_textures[i]->getMinMipMap(); + } else { + assert(false); } } } @@ -59,21 +61,12 @@ bool KRTextureCube::createGLTexture(int lod_max_dim) assert(m_iNewHandle == m_iHandle); // Only allow one resize per frame bool success = true; - int prev_lod_max_dim = 0; -#if GL_APPLE_copy_texture_levels && GL_EXT_texture_storage - - if(m_iHandle != 0) { - prev_lod_max_dim = m_current_lod_max_dim; - } - - -#endif m_iNewHandle = 0; GLDEBUG(glGenTextures(1, &m_iNewHandle)); assert(m_iNewHandle != 0); - m_current_lod_max_dim = 0; + m_new_lod_max_dim = 0; GLDEBUG(glBindTexture(GL_TEXTURE_CUBE_MAP, m_iNewHandle)); bool bMipMaps = false; @@ -82,7 +75,7 @@ bool KRTextureCube::createGLTexture(int lod_max_dim) std::string faceName = getName() + SUFFIXES[i]; if(m_textures[i]) { if(m_textures[i]->hasMipmaps()) bMipMaps = true; - m_textures[i]->uploadTexture(TARGETS[i], lod_max_dim, m_current_lod_max_dim, prev_lod_max_dim); + m_textures[i]->uploadTexture(TARGETS[i], lod_max_dim, m_new_lod_max_dim); } } @@ -112,7 +105,7 @@ long KRTextureCube::getMemRequiredForSize(int max_dim) return memoryRequired; } - +/* void KRTextureCube::resetPoolExpiry(float lodCoverage, texture_usage_t textureUsage) { KRTexture::resetPoolExpiry(lodCoverage, textureUsage); @@ -122,6 +115,7 @@ void KRTextureCube::resetPoolExpiry(float lodCoverage, texture_usage_t textureUs } } } +*/ void KRTextureCube::bind(GLuint texture_unit) { diff --git a/KREngine/kraken/KRTextureCube.h b/KREngine/kraken/KRTextureCube.h index 95c29fd..c643e2b 100644 --- a/KREngine/kraken/KRTextureCube.h +++ b/KREngine/kraken/KRTextureCube.h @@ -46,7 +46,7 @@ public: virtual void bind(GLuint texture_unit); virtual long getMemRequiredForSize(int max_dim); - virtual void resetPoolExpiry(float lodCoverage, texture_usage_t textureUsage); +// virtual void resetPoolExpiry(float lodCoverage, texture_usage_t textureUsage); private: virtual bool createGLTexture(int lod_max_dim); diff --git a/KREngine/kraken/KRTextureKTX.cpp b/KREngine/kraken/KRTextureKTX.cpp index 863c624..a1bb5bd 100644 --- a/KREngine/kraken/KRTextureKTX.cpp +++ b/KREngine/kraken/KRTextureKTX.cpp @@ -153,7 +153,7 @@ long KRTextureKTX::getMemRequiredForSize(int max_dim) return memoryRequired; } -bool KRTextureKTX::uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, int prev_lod_max_dim, bool compress, bool premultiply_alpha) +bool KRTextureKTX::uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, bool compress, bool premultiply_alpha) { int target_dim = lod_max_dim; if(target_dim < m_min_lod_max_dim) target_dim = m_min_lod_max_dim; @@ -220,7 +220,7 @@ bool KRTextureKTX::uploadTexture(GLenum target, int lod_max_dim, int ¤t_lo current_lod_max_dim = height; } #if GL_APPLE_copy_texture_levels && GL_EXT_texture_storage - if(target == GL_TEXTURE_2D && width <= prev_lod_max_dim && height <= prev_lod_max_dim) { + if(target == GL_TEXTURE_2D && width <= m_current_lod_max_dim && height <= m_current_lod_max_dim) { //GLDEBUG(glCompressedTexImage2D(target, i, (GLenum)m_header.glInternalFormat, width, height, 0, block.length, NULL)); // Allocate, but don't copy // GLDEBUG(glTexImage2D(target, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL)); GLDEBUG(glCopyTextureLevelsAPPLE(m_iNewHandle, m_iHandle, source_level, 1)); @@ -253,7 +253,7 @@ bool KRTextureKTX::uploadTexture(GLenum target, int lod_max_dim, int ¤t_lo destination_level++; } - if(width <= prev_lod_max_dim && height <= prev_lod_max_dim) { + if(width <= m_current_lod_max_dim && height <= m_current_lod_max_dim) { source_level++; } diff --git a/KREngine/kraken/KRTextureKTX.h b/KREngine/kraken/KRTextureKTX.h index f9f5d30..4f1279e 100644 --- a/KREngine/kraken/KRTextureKTX.h +++ b/KREngine/kraken/KRTextureKTX.h @@ -19,7 +19,7 @@ public: virtual ~KRTextureKTX(); virtual std::string getExtension(); - bool uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, int prev_lod_max_dim, bool compress = false, bool premultiply_alpha = false); + bool uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, bool compress = false, bool premultiply_alpha = false); virtual long getMemRequiredForSize(int max_dim); diff --git a/KREngine/kraken/KRTextureManager.cpp b/KREngine/kraken/KRTextureManager.cpp index c6458e9..6222772 100644 --- a/KREngine/kraken/KRTextureManager.cpp +++ b/KREngine/kraken/KRTextureManager.cpp @@ -145,10 +145,42 @@ KRTexture *KRTextureManager::getTextureCube(const char *szName) { unordered_map::iterator itr = m_textures.find(lowerName); if(itr == m_textures.end()) { - KRTextureCube *pTexture = new KRTextureCube(getContext(), lowerName); - m_textures[lowerName] = pTexture; - return pTexture; + // Defer resolving the texture cube until its referenced textures are ready + const GLenum TARGETS[6] = { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z + }; + + const char *SUFFIXES[6] = { + "_positive_x", + "_negative_x", + "_positive_y", + "_negative_y", + "_positive_z", + "_negative_z" + }; + bool found_all = true; + for(int i=0; i<6; i++) { + std::string faceName = lowerName + SUFFIXES[i]; + KRTexture *faceTexture = dynamic_cast(getContext().getTextureManager()->getTexture(faceName)); + if(faceTexture == NULL) { + found_all = false; + } + } + + if(found_all) { + KRTextureCube *pTexture = new KRTextureCube(getContext(), lowerName); + + m_textures[lowerName] = pTexture; + return pTexture; + } else { + return NULL; + } } else { return (*itr).second; } @@ -251,7 +283,7 @@ void KRTextureManager::doStreaming() { // TODO - Implement proper double-buffering to reduce copy operations m_streamerFenceMutex.lock(); - m_activeTextures_streamer = m_activeTextures_streamer_copy; + m_activeTextures_streamer = std::move(m_activeTextures_streamer_copy); m_streamerFenceMutex.unlock(); balanceTextureMemory(); @@ -277,15 +309,8 @@ void KRTextureManager::balanceTextureMemory() // --------------- - /* - // TODO - Would this be faster with int's for weights? - std::vector > sortedTextures; - for(auto itr=m_activeTextures_streamer.begin(); itr != m_activeTextures_streamer.end(); itr++) { - KRTexture *texture = *itr; - float priority = texture->getStreamPriority(); - sortedTextures.push_back(std::pair(priority, texture)); - } - */ + //long MAX_STREAM_TIME = 66; + //long startTime = getContext().getAbsoluteTimeMilliseconds(); std::sort(m_activeTextures_streamer.begin(), m_activeTextures_streamer.end(), std::greater>()); @@ -298,12 +323,14 @@ void KRTextureManager::balanceTextureMemory() long minLodMem = texture->getMemRequiredForSize(min_mip_level); memoryRemaining -= minLodMem; - if(memoryRemainingThisFrame > minLodMem && texture->getCurrentLodMaxDim() < min_mip_level) { + if(memoryRemainingThisFrame > minLodMem && texture->getNewLodMaxDim() < min_mip_level) { memoryRemainingThisFrame -= minLodMem; texture->resize(min_mip_level); } } + //long minMipTime = getContext().getAbsoluteTimeMilliseconds() - startTime; + std::vector mipPercents = {75, 75, 50, 50, 50}; int mip_drop = -1; auto mip_itr = mipPercents.begin(); @@ -329,13 +356,26 @@ void KRTextureManager::balanceTextureMemory() memoryRemainingThisMip -= additionalMemRequired; memoryRemaining -= additionalMemRequired; if(memoryRemainingThisMip > 0 && memoryRemainingThisFrame > targetMem) { - if(texture->getCurrentLodMaxDim() != target_mip_level) { + int current_mip_level = texture->getNewLodMaxDim(); + if(current_mip_level == (target_mip_level >> 1) || target_mip_level < current_mip_level) { memoryRemainingThisFrame -= targetMem; texture->resize(target_mip_level); + } else if(current_mip_level == (target_mip_level >> 2)) { + memoryRemainingThisFrame -= texture->getMemRequiredForSize(target_mip_level >> 1); + texture->resize(target_mip_level >> 1); + } else if(current_mip_level < (target_mip_level >> 2)) { + memoryRemainingThisFrame -= texture->getMemRequiredForSize(target_mip_level >> 2); + texture->resize(target_mip_level >> 2); } } + + //if(getContext().getAbsoluteTimeMilliseconds() - startTime > MAX_STREAM_TIME) { + // return; // Bail out early if we spend too long + //} } + //long streamerTime = getContext().getAbsoluteTimeMilliseconds() - startTime; + //fprintf(stderr, "%i / %i\n", (int)minMipTime, (int)streamerTime); } diff --git a/KREngine/kraken/KRTexturePVR.cpp b/KREngine/kraken/KRTexturePVR.cpp index 46f3ef4..24de4b4 100644 --- a/KREngine/kraken/KRTexturePVR.cpp +++ b/KREngine/kraken/KRTexturePVR.cpp @@ -173,7 +173,7 @@ long KRTexturePVR::getMemRequiredForSize(int max_dim) return memoryRequired; } -bool KRTexturePVR::uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, int prev_lod_max_dim, bool compress, bool premultiply_alpha) +bool KRTexturePVR::uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, bool compress, bool premultiply_alpha) { int target_dim = lod_max_dim; if(target_dim < m_min_lod_max_dim) target_dim = m_min_lod_max_dim; @@ -240,7 +240,7 @@ bool KRTexturePVR::uploadTexture(GLenum target, int lod_max_dim, int ¤t_lo current_lod_max_dim = height; } #if GL_APPLE_copy_texture_levels && GL_EXT_texture_storage - if(target == GL_TEXTURE_2D && width <= prev_lod_max_dim && height <= prev_lod_max_dim) { + if(target == GL_TEXTURE_2D && width <= m_current_lod_max_dim && height <= m_current_lod_max_dim) { //GLDEBUG(glCompressedTexImage2D(target, i, m_internalFormat, width, height, 0, block.length, NULL)); // Allocate, but don't copy // GLDEBUG(glTexImage2D(target, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL)); GLDEBUG(glCopyTextureLevelsAPPLE(m_iNewHandle, m_iHandle, source_level, 1)); @@ -273,7 +273,7 @@ bool KRTexturePVR::uploadTexture(GLenum target, int lod_max_dim, int ¤t_lo destination_level++; } - if(width <= prev_lod_max_dim && height <= prev_lod_max_dim) { + if(width <= m_current_lod_max_dim && height <= m_current_lod_max_dim) { source_level++; } diff --git a/KREngine/kraken/KRTexturePVR.h b/KREngine/kraken/KRTexturePVR.h index 3d973d4..0914960 100644 --- a/KREngine/kraken/KRTexturePVR.h +++ b/KREngine/kraken/KRTexturePVR.h @@ -18,7 +18,7 @@ public: virtual ~KRTexturePVR(); virtual std::string getExtension(); - bool uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, int prev_lod_max_dim, bool compress = false, bool premultiply_alpha = false); + bool uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, bool compress = false, bool premultiply_alpha = false); virtual long getMemRequiredForSize(int max_dim); diff --git a/KREngine/kraken/KRTextureTGA.cpp b/KREngine/kraken/KRTextureTGA.cpp index b8c97fb..e7f1d3c 100644 --- a/KREngine/kraken/KRTextureTGA.cpp +++ b/KREngine/kraken/KRTextureTGA.cpp @@ -71,7 +71,7 @@ KRTextureTGA::~KRTextureTGA() } -bool KRTextureTGA::uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, int prev_lod_max_dim, bool compress, bool premultiply_alpha) +bool KRTextureTGA::uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, bool compress, bool premultiply_alpha) { m_pData->lock(); TGA_HEADER *pHeader = (TGA_HEADER *)m_pData->getStart(); @@ -298,7 +298,7 @@ KRTexture *KRTextureTGA::compress(bool premultiply_alpha) GLDEBUG(glBindTexture(GL_TEXTURE_2D, compressed_handle)); int current_max_dim = 0; - if(!uploadTexture(GL_TEXTURE_2D, m_max_lod_max_dim, current_max_dim, 0, true, premultiply_alpha)) { + if(!uploadTexture(GL_TEXTURE_2D, m_max_lod_max_dim, current_max_dim, true, premultiply_alpha)) { assert(false); // Failed to upload the texture } GLDEBUG(glGenerateMipmap(GL_TEXTURE_2D)); diff --git a/KREngine/kraken/KRTextureTGA.h b/KREngine/kraken/KRTextureTGA.h index abca2a8..18e992a 100644 --- a/KREngine/kraken/KRTextureTGA.h +++ b/KREngine/kraken/KRTextureTGA.h @@ -18,7 +18,7 @@ public: virtual ~KRTextureTGA(); virtual std::string getExtension(); - bool uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, int prev_lod_max_dim, bool compress = false, bool premultiply_alpha = false); + bool uploadTexture(GLenum target, int lod_max_dim, int ¤t_lod_max_dim, bool compress = false, bool premultiply_alpha = false); #if !TARGET_OS_IPHONE virtual KRTexture *compress(bool premultiply_alpha = false);