Updated texture memory management code to eliminate inter-frame texture swapping and glFinish() calls, favouring performance over maximum texture resolution when memory is insufficient for textures at full resolution.

--HG--
extra : convert_revision : svn%3A7752d6cf-9f14-4ad2-affc-04f1e67b81a5/trunk%40159
This commit is contained in:
kearwood
2012-11-17 00:15:52 +00:00
parent c30c9725e9
commit a0549b4cfb
23 changed files with 406 additions and 180 deletions

View File

@@ -519,10 +519,10 @@
E491018013C99BDC0098455B /* KRTextureManager.cpp */,
E491018613C99BDC0098455B /* KRTexture2D.h */,
E491018113C99BDC0098455B /* KRTexture2D.cpp */,
E4B175AA161F5A1000B8FB80 /* KRTexture.cpp */,
E4B175AB161F5A1000B8FB80 /* KRTexture.h */,
E4B175B0161F5FAE00B8FB80 /* KRTextureCube.cpp */,
E4B175AA161F5A1000B8FB80 /* KRTexture.cpp */,
E4B175B1161F5FAF00B8FB80 /* KRTextureCube.h */,
E4B175B0161F5FAE00B8FB80 /* KRTextureCube.cpp */,
E4CA10E41637BD0A005D9400 /* KRTexturePVR.h */,
E4CA10E81637BD2B005D9400 /* KRTexturePVR.cpp */,
E4CA10EB1637BD47005D9400 /* KRTextureTGA.h */,

View File

@@ -163,8 +163,6 @@ void KRCamera::renderFrame(KRScene &scene, KRMat4 &viewMatrix, float deltaTime)
setViewportSize(KRVector2(backingWidth, backingHeight));
m_viewport = KRViewport(getViewportSize(), viewMatrix, getProjectionMatrix());
m_pContext->rotateBuffers(true);
renderFrame(scene, deltaTime);
@@ -232,10 +230,10 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
GLDEBUG(glDepthMask(GL_FALSE));
// Set source to buffers from pass 1
m_pContext->getTextureManager()->selectTexture(6, NULL, 0);
m_pContext->getTextureManager()->selectTexture(6, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE6));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeColorTexture));
m_pContext->getTextureManager()->selectTexture(7, NULL, 0);
m_pContext->getTextureManager()->selectTexture(7, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE7));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeDepthTexture));
@@ -255,7 +253,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
GLDEBUG(glClear(GL_COLOR_BUFFER_BIT));
// Set source to buffers from pass 2
m_pContext->getTextureManager()->selectTexture(6, NULL, 0);
m_pContext->getTextureManager()->selectTexture(6, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE6));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, lightAccumulationTexture));
@@ -276,10 +274,10 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
scene.render(this, emptyBoundsSet, m_viewport, KRNode::RENDER_PASS_DEFERRED_OPAQUE, newVisibleBounds);
// Deactivate source buffer texture units
m_pContext->getTextureManager()->selectTexture(6, NULL, 0);
m_pContext->getTextureManager()->selectTexture(6, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE6));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
m_pContext->getTextureManager()->selectTexture(7, NULL, 0);
m_pContext->getTextureManager()->selectTexture(7, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE7));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
} else {
@@ -342,7 +340,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
if(m_pSkyBoxTexture) {
getContext().getShaderManager()->selectShader("sky_box", this, std::vector<KRLight *>(), m_viewport, KRMat4(), false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_OPAQUE);
getContext().getTextureManager()->selectTexture(0, m_pSkyBoxTexture, 2048);
getContext().getTextureManager()->selectTexture(0, m_pSkyBoxTexture);
// Render a full screen quad
m_pContext->getModelManager()->bindVBO((void *)KRENGINE_VBO_2D_SQUARE, KRENGINE_VBO_2D_SQUARE_SIZE, true, false, false, true, false);
@@ -414,7 +412,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
// Disable z-buffer test
GLDEBUG(glDisable(GL_DEPTH_TEST));
m_pContext->getTextureManager()->selectTexture(0, NULL, 0);
m_pContext->getTextureManager()->selectTexture(0, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE0));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeDepthTexture));
@@ -665,16 +663,16 @@ void KRCamera::renderPost()
KRShader *postShader = m_pContext->getShaderManager()->getShader("PostShader", this, std::vector<KRLight *>(), false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_TRANSPARENT);
getContext().getShaderManager()->selectShader(postShader, m_viewport, KRMat4(), std::vector<KRLight *>(), KRNode::RENDER_PASS_FORWARD_TRANSPARENT);
m_pContext->getTextureManager()->selectTexture(0, NULL, 0);
m_pContext->getTextureManager()->selectTexture(0, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE0));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeDepthTexture));
m_pContext->getTextureManager()->selectTexture(1, NULL, 0);
m_pContext->getTextureManager()->selectTexture(1, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE1));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeColorTexture));
if(volumetric_environment_enable) {
m_pContext->getTextureManager()->selectTexture(2, NULL, 0);
m_pContext->getTextureManager()->selectTexture(2, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE2));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, volumetricLightAccumulationTexture));
}
@@ -684,11 +682,11 @@ void KRCamera::renderPost()
GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
m_pContext->getTextureManager()->selectTexture(0, NULL, 0);
m_pContext->getTextureManager()->selectTexture(0, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE0));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
m_pContext->getTextureManager()->selectTexture(1, NULL, 0);
m_pContext->getTextureManager()->selectTexture(1, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE1));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
@@ -701,7 +699,7 @@ void KRCamera::renderPost()
// viewMatrix.scale(0.20, 0.20, 0.20);
// viewMatrix.translate(-0.70, 0.70 - 0.45 * iShadow, 0.0);
// getContext().getShaderManager()->selectShader(blitShader, KRViewport(getViewportSize(), viewMatrix, KRMat4()), shadowViewports, KRMat4(), KRVector3(), NULL, 0, KRNode::RENDER_PASS_FORWARD_TRANSPARENT);
// m_pContext->getTextureManager()->selectTexture(1, NULL, 0);
// m_pContext->getTextureManager()->selectTexture(1, NULL);
// m_pContext->getModelManager()->bindVBO((void *)KRENGINE_VBO_2D_SQUARE, KRENGINE_VBO_2D_SQUARE_SIZE, true, false, false, true, false);
// GLDEBUG(glActiveTexture(GL_TEXTURE0));
// GLDEBUG(glBindTexture(GL_TEXTURE_2D, shadowDepthTexture[iShadow]));
@@ -714,7 +712,7 @@ void KRCamera::renderPost()
//#endif
// }
//
// m_pContext->getTextureManager()->selectTexture(0, NULL, 0);
// m_pContext->getTextureManager()->selectTexture(0, NULL);
// GLDEBUG(glActiveTexture(GL_TEXTURE0));
// GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
// }
@@ -725,7 +723,7 @@ void KRCamera::renderPost()
if(*szText) {
KRShader *fontShader = m_pContext->getShaderManager()->getShader("debug_font", this, std::vector<KRLight *>(), false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, KRNode::RENDER_PASS_FORWARD_TRANSPARENT);
m_pContext->getTextureManager()->selectTexture(0, m_pContext->getTextureManager()->getTexture("font"), 2048);
m_pContext->getTextureManager()->selectTexture(0, m_pContext->getTextureManager()->getTexture("font"));
const char *pChar = szText;
int iPos=0;
@@ -763,7 +761,7 @@ void KRCamera::renderPost()
GLDEBUG(glActiveTexture(GL_TEXTURE0));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
m_pContext->getTextureManager()->selectTexture(1, NULL, 0);
m_pContext->getTextureManager()->selectTexture(1, NULL);
}
}

View File

@@ -20,6 +20,7 @@ int KRContext::KRENGINE_TARGET_TEXTURE_MEM_MAX;
int KRContext::KRENGINE_TARGET_TEXTURE_MEM_MIN;
int KRContext::KRENGINE_MAX_TEXTURE_DIM;
int KRContext::KRENGINE_MIN_TEXTURE_DIM;
int KRContext::KRENGINE_MAX_TEXTURE_THROUGHPUT;
const char *KRContext::extension_names[KRENGINE_NUM_EXTENSIONS] = {
"GL_EXT_texture_storage"
@@ -33,6 +34,7 @@ KRContext::KRContext() {
m_pModelManager = new KRModelManager(*this);
m_pSceneManager = new KRSceneManager(*this);
m_bDetectedExtensions = false;
m_current_frame = 0;
}
KRContext::~KRContext() {
@@ -130,13 +132,29 @@ void KRContext::loadResource(std::string path) {
void KRContext::rotateBuffers(bool new_frame) {
//fprintf(stderr, "Rotating Buffers...\n");
GLDEBUG(glFinish());
if(!new_frame) GLDEBUG(glFinish());
m_pModelManager->rotateBuffers(new_frame);
m_pTextureManager->rotateBuffers(new_frame);
}
void KRContext::detectExtensions() {
m_bDetectedExtensions = true;
}
}
void KRContext::startFrame()
{
m_pTextureManager->startFrame();
}
void KRContext::endFrame()
{
m_pTextureManager->endFrame();
rotateBuffers(true);
m_current_frame++;
}
long KRContext::getCurrentFrame()
{
return m_current_frame;
}

View File

@@ -28,6 +28,7 @@ public:
static int KRENGINE_TARGET_TEXTURE_MEM_MIN;
static int KRENGINE_MAX_TEXTURE_DIM;
static int KRENGINE_MIN_TEXTURE_DIM;
static int KRENGINE_MAX_TEXTURE_THROUGHPUT;
KRContext();
@@ -55,6 +56,11 @@ public:
static const char * extension_names[KRENGINE_NUM_EXTENSIONS];
static bool extension_available[KRENGINE_NUM_EXTENSIONS];
void startFrame();
void endFrame();
long getCurrentFrame();
private:
KRBundleManager *m_pBundleManager;
KRSceneManager *m_pSceneManager;
@@ -65,6 +71,8 @@ private:
void detectExtensions();
bool m_bDetectedExtensions;
long m_current_frame;
};
#endif

View File

@@ -79,6 +79,7 @@ float const PI = 3.141592653589793f;
KRContext::KRENGINE_TARGET_TEXTURE_MEM_MIN = 32000000 * 2;
KRContext::KRENGINE_MAX_TEXTURE_DIM = 2048;
KRContext::KRENGINE_MIN_TEXTURE_DIM = 64;
KRContext::KRENGINE_MAX_TEXTURE_THROUGHPUT = 32000000;
} else {
KRContext::KRENGINE_MAX_VBO_HANDLES = 10000;
KRContext::KRENGINE_MAX_VBO_MEM = 128000000;
@@ -89,6 +90,7 @@ float const PI = 3.141592653589793f;
KRContext::KRENGINE_TARGET_TEXTURE_MEM_MIN = 32000000;
KRContext::KRENGINE_MAX_TEXTURE_DIM = 2048;
KRContext::KRENGINE_MIN_TEXTURE_DIM = 64;
KRContext::KRENGINE_MAX_TEXTURE_THROUGHPUT = 32000000;
}
_camera = NULL;
@@ -158,7 +160,9 @@ float const PI = 3.141592653589793f;
- (void)renderScene: (KRScene *)pScene WithViewMatrix: (KRMat4)viewMatrix AndDeltaTime: (float)deltaTime
{
_context->startFrame();
_camera->renderFrame(*pScene, viewMatrix, deltaTime);
_context->endFrame();
}
- (BOOL)loadShaders

View File

@@ -107,7 +107,7 @@ void KRInstance::render(KRCamera *pCamera, std::vector<KRLight *> &lights, const
}
if(m_pLightMap && pCamera->bEnableLightMap && renderPass != RENDER_PASS_SHADOWMAP && renderPass != RENDER_PASS_GENERATE_SHADOWMAPS) {
m_pContext->getTextureManager()->selectTexture(5, m_pLightMap, 2048);
m_pContext->getTextureManager()->selectTexture(5, m_pLightMap);
}
pModel->render(pCamera, lights, viewport, getModelMatrix(), m_pLightMap, renderPass);

View File

@@ -190,7 +190,7 @@ void KRLight::render(KRCamera *pCamera, std::vector<KRLight *> &lights, const KR
pShader->m_uniforms[KRShader::KRENGINE_UNIFORM_FLARE_SIZE],
m_flareSize
));
m_pContext->getTextureManager()->selectTexture(0, m_pFlareTexture, 2048);
m_pContext->getTextureManager()->selectTexture(0, m_pFlareTexture);
m_pContext->getModelManager()->bindVBO((void *)KRENGINE_VBO_2D_SQUARE, KRENGINE_VBO_2D_SQUARE_SIZE, true, false, false, true, false);
GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
}
@@ -270,6 +270,12 @@ void KRLight::renderShadowBuffers(KRCamera *pCamera)
glViewport(0, 0, m_shadowViewports[iShadow].getSize().x, m_shadowViewports[iShadow].getSize().y);
GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, shadowFramebuffer[iShadow]));
GLDEBUG(glClearDepthf(0.0f));
GLDEBUG(glClear(GL_DEPTH_BUFFER_BIT));
glViewport(0, 0, m_shadowViewports[iShadow].getSize().x, m_shadowViewports[iShadow].getSize().y);
GLDEBUG(glClearDepthf(1.0f));
GLDEBUG(glClear(GL_DEPTH_BUFFER_BIT));

View File

@@ -343,24 +343,24 @@ bool KRMaterial::bind(KRMaterial **prevBoundMaterial, char *szPrevShaderKey, con
GLDEBUG(glUniform1f(pShader->m_uniforms[KRShader::KRENGINE_UNIFORM_MATERIAL_ALPHA], m_tr));
if(bDiffuseMap) {
m_pContext->getTextureManager()->selectTexture(0, m_pDiffuseMap, 2048);
m_pContext->getTextureManager()->selectTexture(0, m_pDiffuseMap);
}
if(bSpecMap) {
m_pContext->getTextureManager()->selectTexture(1, m_pSpecularMap, 2048);
m_pContext->getTextureManager()->selectTexture(1, m_pSpecularMap);
}
if(bNormalMap) {
m_pContext->getTextureManager()->selectTexture(2, m_pNormalMap, 2048);
m_pContext->getTextureManager()->selectTexture(2, m_pNormalMap);
}
if(bReflectionCubeMap && (renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE || renderPass == KRNode::RENDER_PASS_DEFERRED_OPAQUE)) {
m_pContext->getTextureManager()->selectTexture(4, m_pReflectionCube, 2048);
m_pContext->getTextureManager()->selectTexture(4, m_pReflectionCube);
}
if(bReflectionMap && (renderPass == KRNode::RENDER_PASS_FORWARD_OPAQUE || renderPass == KRNode::RENDER_PASS_DEFERRED_OPAQUE)) {
// GL_TEXTURE7 is used for reading the depth buffer in gBuffer pass 2 and re-used for the reflection map in gBuffer Pass 3 and in forward rendering
m_pContext->getTextureManager()->selectTexture(7, m_pReflectionMap, 2048);
m_pContext->getTextureManager()->selectTexture(7, m_pReflectionMap);
}
*prevBoundMaterial = this;

View File

@@ -124,9 +124,9 @@ void KRModelManager::bindVBO(GLvoid *data, GLsizeiptr size, bool enable_vertex,
configureAttribs(enable_vertex, enable_normal, enable_tangent, enable_uva, enable_uvb);
#endif
} else {
m_vboMemUsed += size;
while(m_vbosPool.size() + m_vbosActive.size() >= KRContext::KRENGINE_MAX_VBO_HANDLES || m_vboMemUsed >= KRContext::KRENGINE_MAX_VBO_MEM) {
while(m_vbosPool.size() + m_vbosActive.size() + 1 >= KRContext::KRENGINE_MAX_VBO_HANDLES || m_vboMemUsed + size >= KRContext::KRENGINE_MAX_VBO_MEM) {
if(m_vbosPool.empty()) {
fprintf(stderr, "flushBuffers due to VBO exhaustion...\n");
m_pContext->rotateBuffers(false);
@@ -152,6 +152,7 @@ void KRModelManager::bindVBO(GLvoid *data, GLsizeiptr size, bool enable_vertex,
GLDEBUG(glBindBuffer(GL_ARRAY_BUFFER, m_currentVBO.vbo_handle));
GLDEBUG(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW));
m_vboMemUsed += size;
configureAttribs(enable_vertex, enable_normal, enable_tangent, enable_uva, enable_uvb);
m_currentVBO.size = size;

View File

@@ -67,7 +67,7 @@ void KRParticleSystemBrownian::render(KRCamera *pCamera, std::vector<KRLight *>
GLDEBUG(glDepthRangef(0.0, 1.0));
KRTexture *pParticleTexture = m_pContext->getTextureManager()->getTexture("flare");
m_pContext->getTextureManager()->selectTexture(0, pParticleTexture, 2048);
m_pContext->getTextureManager()->selectTexture(0, pParticleTexture);
int particle_count = 10000;

View File

@@ -238,7 +238,7 @@ bool KRShader::bind(const KRViewport &viewport, const KRMat4 &matModel, const st
if(light_directional_count == 0) {
int cShadowBuffers = directional_light->getShadowBufferCount();
if(m_uniforms[KRENGINE_UNIFORM_SHADOWTEXTURE1] != -1 && cShadowBuffers > 0) {
m_pContext->getTextureManager()->selectTexture(3, NULL, 0);
m_pContext->getTextureManager()->selectTexture(3, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE3));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, directional_light->getShadowTextures()[0]));
GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
@@ -248,7 +248,7 @@ bool KRShader::bind(const KRViewport &viewport, const KRMat4 &matModel, const st
}
if(m_uniforms[KRENGINE_UNIFORM_SHADOWTEXTURE2] != -1 && cShadowBuffers > 1) {
m_pContext->getTextureManager()->selectTexture(4, NULL, 0);
m_pContext->getTextureManager()->selectTexture(4, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE4));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, directional_light->getShadowTextures()[1]));
GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
@@ -258,7 +258,7 @@ bool KRShader::bind(const KRViewport &viewport, const KRMat4 &matModel, const st
}
if(m_uniforms[KRENGINE_UNIFORM_SHADOWTEXTURE3] != -1 && cShadowBuffers > 2) {
m_pContext->getTextureManager()->selectTexture(5, NULL, 0);
m_pContext->getTextureManager()->selectTexture(5, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE5));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, directional_light->getShadowTextures()[2]));
GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));

View File

@@ -6,60 +6,104 @@
// Copyright (c) 2012 Kearwood Software. All rights reserved.
//
#include <assert.h>
#include "KRTexture.h"
#include "KRDataBlock.h"
#include <assert.h>
#include "KRContext.h"
#include "KRTextureManager.h"
KRTexture::KRTexture(KRContext &context) : KRContextObject(context)
{
m_iHandle = 0;
m_textureMemUsed = 0;
m_last_frame_used = 0;
}
KRTexture::~KRTexture()
{
size_t textureMemFreed = 0;
releaseHandle(textureMemFreed);
releaseHandle();
}
void KRTexture::releaseHandle(size_t &textureMemUsed) {
textureMemUsed -= getMemSize();
void KRTexture::releaseHandle() {
if(m_iHandle != 0) {
GLDEBUG(glDeleteTextures(1, &m_iHandle));
getContext().getTextureManager()->memoryChanged(-getMemSize());
m_iHandle = 0;
m_textureMemUsed = 0;
}
textureMemUsed += getMemSize();
}
long KRTexture::getMemSize() {
return m_textureMemUsed; // TODO - This is not 100% accurate, as loaded format may differ in size while in GPU memory
}
GLuint KRTexture::getHandle(int max_dim, bool can_resize) {
// Constrain target LOD to be within mipmap levels of texture
int target_dim = max_dim;
// ---- Start: incremental texture loading ----
if(m_current_lod_max_dim == 0) {
target_dim = m_min_lod_max_dim;
} else if((m_current_lod_max_dim << 1) < target_dim) {
target_dim = m_current_lod_max_dim << 1;
}
// ---- End: incremental texture loading ----
if(target_dim < m_min_lod_max_dim) target_dim = m_min_lod_max_dim;
if(target_dim > m_max_lod_max_dim) target_dim = m_max_lod_max_dim;
if(can_resize && m_current_lod_max_dim != target_dim) {
size_t memFreed = 0;
releaseHandle(memFreed);
}
if(m_iHandle == 0) {
if(!createGLTexture(target_dim)) {
assert(false);
void KRTexture::resize(int max_dim)
{
if(max_dim == 0) {
releaseHandle();
} else {
int requiredMemoryTransfer = getThroughputRequiredForResize(max_dim);
int requiredMemoryDelta = getMemRequiredForSize(max_dim) - getMemSize();
if(requiredMemoryDelta) {
// Only resize / regenerate the texture if it actually changes the size of the texture (Assumption: textures of different sizes will always consume different amounts of memory)
if(getContext().getTextureManager()->getMemoryTransferedThisFrame() + requiredMemoryTransfer > getContext().KRENGINE_MAX_TEXTURE_THROUGHPUT) {
// Exceeding per-frame transfer throughput; can't resize now
return;
}
if(getContext().getTextureManager()->getMemUsed() + requiredMemoryDelta > getContext().KRENGINE_MAX_TEXTURE_MEM) {
// Exceeding total memory allocated to textures; can't resize now
return;
}
if(m_current_lod_max_dim != max_dim || m_iHandle == 0) {
releaseHandle();
}
if(m_iHandle == 0) {
if(!createGLTexture(max_dim)) {
assert(false);
}
}
}
}
}
GLuint KRTexture::getHandle() {
if(m_iHandle == 0) {
resize(getContext().KRENGINE_MIN_TEXTURE_DIM);
}
m_last_frame_used = getContext().getCurrentFrame();
return m_iHandle;
}
long KRTexture::getThroughputRequiredForResize(int max_dim)
{
// Calculate the throughput required for GPU texture upload if the texture is resized to max_dim.
// This default behaviour assumes that the texture will need to be deleted and regenerated to change the maximum mip-map level.
// If an OpenGL extension is present that allows a texture to be resized incrementally, then this method should be overridden
if(max_dim != m_current_lod_max_dim && max_dim != 0) {
int requiredMemory = getMemRequiredForSize(max_dim);
int requiredMemoryDelta = requiredMemory - getMemSize();
if(requiredMemoryDelta == 0) {
// Only resize / regenerate the texture if it actually changes the size of the texture (Assumption: textures of different sizes will always consume different amounts of memory)
return 0;
}
return requiredMemory;
} else {
return 0;
}
}
long KRTexture::getLastFrameUsed()
{
return m_last_frame_used;
}

View File

@@ -45,24 +45,30 @@ public:
KRTexture(KRContext &context);
virtual ~KRTexture();
virtual void bind(size_t &textureMemUsed, int max_dim, bool can_resize) = 0;
void releaseHandle(size_t &textureMemUsed);
virtual void bind() = 0;
void releaseHandle();
long getMemSize();
virtual long getMemRequiredForSize(int max_dim) = 0;
virtual long getThroughputRequiredForResize(int max_dim);
virtual void resize(int max_dim);
long getLastFrameUsed();
protected:
virtual bool createGLTexture(int lod_max_dim) = 0;
GLuint getHandle(int max_dim, bool can_resize);
GLuint getHandle();
GLuint m_iHandle;
size_t m_textureMemUsed;
long m_textureMemUsed;
int m_current_lod_max_dim;
uint32_t m_max_lod_max_dim;
uint32_t m_min_lod_max_dim;
long m_last_frame_used;
};

View File

@@ -63,6 +63,7 @@ bool KRTexture2D::createGLTexture(int lod_max_dim) {
} else {
GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
}
if(!uploadTexture(GL_TEXTURE_2D, lod_max_dim, m_current_lod_max_dim, m_textureMemUsed)) {
GLDEBUG(glDeleteTextures(1, &m_iHandle));
m_iHandle = 0;
@@ -70,19 +71,20 @@ bool KRTexture2D::createGLTexture(int lod_max_dim) {
return false;
}
return true;
}
void KRTexture2D::bind(size_t &textureMemUsed, int max_dim, bool can_resize) {
textureMemUsed -= getMemSize();
GLDEBUG(glBindTexture(GL_TEXTURE_2D, getHandle(max_dim, can_resize)));
void KRTexture2D::bind() {
GLuint handle = getHandle();
// TODO - These texture parameters should be assigned by the material or texture parameters
GLDEBUG(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f));
GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT));
GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT));
textureMemUsed += getMemSize();
GLDEBUG(glBindTexture(GL_TEXTURE_2D, handle));
if(handle) {
// TODO - These texture parameters should be assigned by the material or texture parameters
GLDEBUG(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f));
GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT));
GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT));
}
}
int KRTexture2D::getMaxMipMap() {
@@ -95,5 +97,4 @@ int KRTexture2D::getMinMipMap() {
bool KRTexture2D::hasMipmaps() {
return m_max_lod_max_dim != m_min_lod_max_dim;
}
}

View File

@@ -52,8 +52,8 @@ public:
int getMaxMipMap();
int getMinMipMap();
virtual bool uploadTexture(GLenum target, int lod_max_dim, int &current_lod_max_dim, size_t &textureMemUsed) = 0;
virtual void bind(size_t &textureMemUsed, int max_dim, bool can_resize);
virtual bool uploadTexture(GLenum target, int lod_max_dim, int &current_lod_max_dim, long &textureMemUsed) = 0;
virtual void bind();
protected:
KRDataBlock *m_pData;

View File

@@ -29,6 +29,7 @@
// or implied, of Kearwood Gilbert.
//
#import <assert.h>
#include "KRTextureCube.h"
#include "KRTexture2D.h"
#include "KRContext.h"
@@ -85,14 +86,27 @@ bool KRTextureCube::createGLTexture(int lod_max_dim)
return true;
}
void KRTextureCube::bind(size_t &textureMemUsed, int max_dim, bool can_resize)
long KRTextureCube::getMemRequiredForSize(int max_dim)
{
textureMemUsed -= getMemSize();
GLDEBUG(glBindTexture(GL_TEXTURE_CUBE_MAP, getHandle(max_dim, can_resize)));
GLDEBUG(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GLDEBUG(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
textureMemUsed += getMemSize();
}
long memoryRequired = 0;
for(int i=0; i<6; i++) {
std::string faceName = m_name + SUFFIXES[i];
KRTexture2D *faceTexture = (KRTexture2D *)getContext().getTextureManager()->getTexture(faceName.c_str());
if(faceTexture) {
memoryRequired += faceTexture->getMemRequiredForSize(max_dim);
}
}
return memoryRequired;
}
void KRTextureCube::bind()
{
GLuint handle = getHandle();
GLDEBUG(glBindTexture(GL_TEXTURE_CUBE_MAP, handle));
if(handle) {
GLDEBUG(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GLDEBUG(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
}
}

View File

@@ -39,7 +39,9 @@ public:
KRTextureCube(KRContext &context, std::string name);
virtual ~KRTextureCube();
virtual void bind(size_t &textureMemUsed, int max_dim, bool can_resize);
virtual void bind();
virtual long getMemRequiredForSize(int max_dim);
private:
virtual bool createGLTexture(int lod_max_dim);

View File

@@ -40,11 +40,10 @@
KRTextureManager::KRTextureManager(KRContext &context) : KRContextObject(context) {
m_textureMemUsed = 0;
m_activeTextureMemUsed = 0;
m_lod_max_dim_cap = 2048;
for(int iTexture=0; iTexture<KRENGINE_MAX_TEXTURE_UNITS; iTexture++) {
m_boundTextures[iTexture] = NULL;
}
m_memoryTransferredThisFrame = 0;
}
KRTextureManager::~KRTextureManager() {
@@ -109,99 +108,144 @@ KRTexture *KRTextureManager::getTexture(const char *szName) {
}
void KRTextureManager::selectTexture(int iTextureUnit, KRTexture *pTexture, int lod_max_dim) {
void KRTextureManager::selectTexture(int iTextureUnit, KRTexture *pTexture) {
if(m_boundTextures[iTextureUnit] != pTexture) {
GLDEBUG(glActiveTexture(GL_TEXTURE0 + iTextureUnit));
if(pTexture != NULL) {
m_poolTextures.erase(pTexture);
bool bActive = true;
if(m_activeTextures.find(pTexture) == m_activeTextures.end()) {
bActive = false;
m_activeTextures.insert(pTexture);
}
size_t textureMemChange = 0;
pTexture->bind(textureMemChange, lod_max_dim < m_lod_max_dim_cap ? lod_max_dim : m_lod_max_dim_cap, !bActive);
m_textureMemUsed += textureMemChange;
if(bActive) {
m_activeTextureMemUsed += textureMemChange;
} else {
m_activeTextureMemUsed += pTexture->getMemSize();
}
pTexture->bind();
} else {
GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
}
m_boundTextures[iTextureUnit] = pTexture;
while(m_activeTextures.size() + m_poolTextures.size() > KRContext::KRENGINE_MAX_TEXTURE_HANDLES || m_textureMemUsed > KRContext::KRContext::KRENGINE_MAX_TEXTURE_MEM) {
if(m_poolTextures.empty()) {
fprintf(stderr, "Kraken - Texture swapping...\n");
decreaseLODCap();
m_pContext->rotateBuffers(false);
}
// Keep texture size within limits
KRTexture *droppedTexture = (*m_poolTextures.begin());
if(droppedTexture == NULL) {
break;
} else {
droppedTexture->releaseHandle(m_textureMemUsed);
m_poolTextures.erase(droppedTexture);
}
}
}
// fprintf(stderr, "VBO Mem: %i Kbyte Texture Mem: %i Kbyte\n", (int)m_pContext->getModelManager()->getMemUsed() / 1024, (int)m_pContext->getTextureManager()->getMemUsed() / 1024);
}
size_t KRTextureManager::getMemUsed() {
long KRTextureManager::getMemUsed() {
return m_textureMemUsed;
}
size_t KRTextureManager::getActiveMemUsed() {
return m_activeTextureMemUsed;
void KRTextureManager::startFrame()
{
m_memoryTransferredThisFrame = 0;
balanceTextureMemory();
rotateBuffers();
}
void KRTextureManager::rotateBuffers(bool new_frame)
void KRTextureManager::endFrame()
{
if(new_frame && m_activeTextureMemUsed < KRContext::KRENGINE_TARGET_TEXTURE_MEM_MIN && m_activeTextureMemUsed * 4 < KRContext::KRENGINE_TARGET_TEXTURE_MEM_MAX) {
// Increasing the LOD level will generally increase active texture memory usage by 4 times, don't increase the texture level until we can ensure that the LOD won't immediately be dropped back to the current level
increaseLODCap();
} else if(new_frame && m_activeTextureMemUsed > KRContext::KRENGINE_TARGET_TEXTURE_MEM_MAX) {
decreaseLODCap();
}
m_poolTextures.insert(m_activeTextures.begin(), m_activeTextures.end());
m_activeTextures.clear();
m_activeTextureMemUsed = 0;
}
void KRTextureManager::balanceTextureMemory()
{
// Balance texture memory by reducing and increasing the maximum mip-map level of both active and inactive textures
// Favour performance over maximum texture resolution when memory is insufficient for textures at full resolution.
for(int iTexture=0; iTexture < KRENGINE_MAX_TEXTURE_UNITS; iTexture++) {
KRTexture *pBoundTexture = m_boundTextures[iTexture];
if(pBoundTexture != NULL) {
m_poolTextures.erase(pBoundTexture);
if(m_activeTextures.find(pBoundTexture) == m_activeTextures.end()) {
m_activeTextures.insert(pBoundTexture);
m_activeTextureMemUsed += pBoundTexture->getMemSize();
// Determine the additional amount of memory required in order to resize all active textures to the maximum size
long wantedTextureMem = 0;
for(std::set<KRTexture *>::iterator itr=m_activeTextures.begin(); itr != m_activeTextures.end(); itr++) {
KRTexture *activeTexture = *itr;
wantedTextureMem = activeTexture->getMemRequiredForSize(getContext().KRENGINE_MAX_TEXTURE_DIM) - activeTexture->getMemSize();
}
// Determine how much memory we need to free up
long memoryDeficit = wantedTextureMem - (getContext().KRENGINE_MAX_TEXTURE_MEM - getMemUsed());
// Determine how many mip map levels we need to strip off of inactive textures to free the memory we need
long maxDimInactive = getContext().KRENGINE_MAX_TEXTURE_DIM;
long potentialMemorySaving = 0;
while(potentialMemorySaving < memoryDeficit && maxDimInactive > getContext().KRENGINE_MIN_TEXTURE_DIM) {
maxDimInactive = maxDimInactive >> 1;
potentialMemorySaving = 0;
for(std::set<KRTexture *>::iterator itr=m_poolTextures.begin(); itr != m_poolTextures.end(); itr++) {
KRTexture *poolTexture = *itr;
long potentialMemoryDelta = poolTexture->getMemRequiredForSize(maxDimInactive) - poolTexture->getMemSize();
if(potentialMemoryDelta < 0) {
potentialMemorySaving += -potentialMemoryDelta;
}
}
}
}
void KRTextureManager::decreaseLODCap()
{
if(m_lod_max_dim_cap > KRContext::KRENGINE_MIN_TEXTURE_DIM) {
m_lod_max_dim_cap = m_lod_max_dim_cap >> 1;
// Strip off mipmap levels of inactive textures to free up memory
long inactive_texture_mem_used_target = 0;
for(std::set<KRTexture *>::iterator itr=m_poolTextures.begin(); itr != m_poolTextures.end(); itr++) {
KRTexture *poolTexture = *itr;
long potentialMemoryDelta = poolTexture->getMemRequiredForSize(maxDimInactive) - poolTexture->getMemSize();
if(potentialMemoryDelta < 0) {
poolTexture->resize(maxDimInactive);
inactive_texture_mem_used_target += poolTexture->getMemRequiredForSize(maxDimInactive);
} else {
inactive_texture_mem_used_target += poolTexture->getMemSize();
}
}
}
void KRTextureManager::increaseLODCap()
{
if(m_lod_max_dim_cap < KRContext::KRENGINE_MAX_TEXTURE_DIM) {
m_lod_max_dim_cap = m_lod_max_dim_cap << 1;
// Determine the maximum mipmap level for the active textures we can achieve with the memory that is available
long memory_available = 0;
long maxDimActive = getContext().KRENGINE_MAX_TEXTURE_DIM;
while(memory_available <= 0 && maxDimActive >= getContext().KRENGINE_MIN_TEXTURE_DIM) {
memory_available = getContext().KRENGINE_MAX_TEXTURE_MEM - inactive_texture_mem_used_target;
for(std::set<KRTexture *>::iterator itr=m_activeTextures.begin(); itr != m_activeTextures.end() && memory_available > 0; itr++) {
KRTexture *activeTexture = *itr;
memory_available -= activeTexture->getMemRequiredForSize(maxDimActive);
}
if(memory_available <= 0) {
maxDimActive = maxDimActive >> 1; // Try the next smaller mipmap size
}
}
// Resize active textures to balance the memory usage and mipmap levels
for(std::set<KRTexture *>::iterator itr=m_activeTextures.begin(); itr != m_activeTextures.end() && memory_available > 0; itr++) {
KRTexture *activeTexture = *itr;
activeTexture->resize(maxDimActive);
}
//fprintf(stderr, "Active mipmap size: %i Inactive mapmap size: %i\n", (int)maxDimActive, (int)maxDimInactive);
}
int KRTextureManager::getLODDimCap()
void KRTextureManager::rotateBuffers()
{
return m_lod_max_dim_cap;
const long KRENGINE_TEXTURE_EXPIRY_FRAMES = 120;
// ----====---- Expire textures that haven't been used in a long time ----====----
std::set<KRTexture *> expiredTextures;
for(std::set<KRTexture *>::iterator itr=m_poolTextures.begin(); itr != m_poolTextures.end(); itr++) {
KRTexture *poolTexture = *itr;
if(poolTexture->getLastFrameUsed() + KRENGINE_TEXTURE_EXPIRY_FRAMES < getContext().getCurrentFrame()) {
expiredTextures.insert(poolTexture);
poolTexture->releaseHandle();
}
}
for(std::set<KRTexture *>::iterator itr=expiredTextures.begin(); itr != expiredTextures.end(); itr++) {
m_poolTextures.erase(*itr);
}
// ----====---- Swap the buffers ----====----
m_poolTextures.insert(m_activeTextures.begin(), m_activeTextures.end());
m_activeTextures.clear();
}
long KRTextureManager::getMemoryTransferedThisFrame()
{
return m_memoryTransferredThisFrame;
}
void KRTextureManager::addMemoryTransferredThisFrame(long memoryTransferred)
{
m_memoryTransferredThisFrame += memoryTransferred;
}
void KRTextureManager::memoryChanged(long memoryDelta)
{
m_textureMemUsed += memoryDelta;
}

View File

@@ -49,22 +49,24 @@ public:
KRTextureManager(KRContext &context);
virtual ~KRTextureManager();
void rotateBuffers(bool new_frame);
void selectTexture(int iTextureUnit, KRTexture *pTexture, int lod_max_dim);
void selectTexture(int iTextureUnit, KRTexture *pTexture);
KRTexture *loadTexture(const char *szName, const char *szExtension, KRDataBlock *data);
KRTexture *getTextureCube(const char *szName);
KRTexture *getTexture(const char *szFile);
size_t getMemUsed();
size_t getActiveMemUsed();
long getMemUsed();
int getLODDimCap();
long getMemoryTransferedThisFrame();
void addMemoryTransferredThisFrame(long memoryTransferred);
void memoryChanged(long memoryDelta);
void startFrame();
void endFrame();
private:
void decreaseLODCap();
void increaseLODCap();
long m_memoryTransferredThisFrame;
std::map<std::string, KRTexture *> m_textures;
@@ -72,10 +74,11 @@ private:
std::set<KRTexture *> m_activeTextures;
std::set<KRTexture *> m_poolTextures;
size_t m_textureMemUsed;
size_t m_activeTextureMemUsed;
long m_textureMemUsed;
int m_lod_max_dim_cap;
void rotateBuffers();
void balanceTextureMemory();
};
#endif

View File

@@ -150,20 +150,53 @@ KRTexturePVR::~KRTexturePVR() {
}
bool KRTexturePVR::uploadTexture(GLenum target, int lod_max_dim, int &current_lod_max_dim, size_t &textureMemUsed)
long KRTexturePVR::getMemRequiredForSize(int max_dim)
{
// Determine how much memory will be consumed
int width = m_iWidth;
int height = m_iHeight;
long memoryRequired = 0;
for(std::list<dataBlockStruct>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
dataBlockStruct block = *itr;
if(width <= max_dim && height <= max_dim) {
memoryRequired += block.length;
}
width = width >> 1;
if(width < 1) {
width = 1;
}
height = height >> 1;
if(height < 1) {
height = 1;
}
}
return memoryRequired;
}
bool KRTexturePVR::uploadTexture(GLenum target, int lod_max_dim, int &current_lod_max_dim, long &textureMemUsed)
{
GLenum err;
if(m_blocks.size() == 0) {
return false;
}
// Determine how much memory will be consumed
int width = m_iWidth;
int height = m_iHeight;
long memoryRequired = 0;
// Upload texture data
int i=0;
for(std::list<dataBlockStruct>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
dataBlockStruct block = *itr;
if(width <= lod_max_dim && height <= lod_max_dim) {
memoryRequired += block.length;
if(width > current_lod_max_dim) {
current_lod_max_dim = width;
}
@@ -173,9 +206,11 @@ bool KRTexturePVR::uploadTexture(GLenum target, int lod_max_dim, int &current_lo
glCompressedTexImage2D(target, i, m_internalFormat, width, height, 0, block.length, block.start);
err = glGetError();
if (err != GL_NO_ERROR) {
assert(false);
return false;
}
textureMemUsed += block.length;
i++;
}
@@ -189,6 +224,10 @@ bool KRTexturePVR::uploadTexture(GLenum target, int lod_max_dim, int &current_lo
}
}
textureMemUsed += memoryRequired;
getContext().getTextureManager()->memoryChanged(memoryRequired);
getContext().getTextureManager()->addMemoryTransferredThisFrame(memoryRequired);
return true;
}

View File

@@ -17,7 +17,9 @@ public:
KRTexturePVR(KRContext &context, KRDataBlock *data);
virtual ~KRTexturePVR();
bool uploadTexture(GLenum target, int lod_max_dim, int &current_lod_max_dim, size_t &textureMemUsed);
bool uploadTexture(GLenum target, int lod_max_dim, int &current_lod_max_dim, long &textureMemUsed);
virtual long getMemRequiredForSize(int max_dim);
protected:

View File

@@ -8,6 +8,7 @@
#include "KRTextureTGA.h"
#include "KREngine-common.h"
#include "KRContext.h"
typedef struct {
char idlength;
@@ -38,10 +39,10 @@ KRTextureTGA::~KRTextureTGA()
}
bool KRTextureTGA::uploadTexture(GLenum target, int lod_max_dim, int &current_lod_max_dim, size_t &textureMemUsed)
bool KRTextureTGA::uploadTexture(GLenum target, int lod_max_dim, int &current_lod_max_dim, long &textureMemUsed)
{
TGA_HEADER *pHeader = (TGA_HEADER *)m_pData->getStart();
unsigned char *pData = (unsigned char *)pHeader + (size_t)pHeader->idlength + (size_t)pHeader->colourmaplength * (size_t)pHeader->colourmaptype + sizeof(TGA_HEADER);
unsigned char *pData = (unsigned char *)pHeader + (long)pHeader->idlength + (long)pHeader->colourmaplength * (long)pHeader->colourmaptype + sizeof(TGA_HEADER);
if(pHeader->colourmaptype != 0) {
return false; // Mapped colors not supported
@@ -74,8 +75,13 @@ bool KRTextureTGA::uploadTexture(GLenum target, int lod_max_dim, int &current_lo
glTexImage2D(target, 0, GL_RGBA, pHeader->width, pHeader->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid *)converted_image);
delete converted_image;
err = glGetError();
if (err != GL_NO_ERROR) return false;
textureMemUsed += pHeader->width * pHeader->height * 4;
if (err != GL_NO_ERROR) {
return false;
}
int memAllocated = pHeader->width * pHeader->height * 4;
textureMemUsed += memAllocated;
getContext().getTextureManager()->memoryChanged(memAllocated);
getContext().getTextureManager()->addMemoryTransferredThisFrame(memAllocated);
current_lod_max_dim = m_max_lod_max_dim;
}
break;
@@ -83,8 +89,13 @@ bool KRTextureTGA::uploadTexture(GLenum target, int lod_max_dim, int &current_lo
{
glTexImage2D(target, 0, GL_RGBA, pHeader->width, pHeader->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid *)pData);
err = glGetError();
if (err != GL_NO_ERROR) return false;
textureMemUsed += pHeader->width * pHeader->height * 4;
if (err != GL_NO_ERROR) {
return false;
}
int memAllocated = pHeader->width * pHeader->height * 4;
textureMemUsed += memAllocated;
getContext().getTextureManager()->memoryChanged(memAllocated);
getContext().getTextureManager()->addMemoryTransferredThisFrame(memAllocated);
current_lod_max_dim = m_max_lod_max_dim;
}
break;
@@ -98,3 +109,26 @@ bool KRTextureTGA::uploadTexture(GLenum target, int lod_max_dim, int &current_lo
return true;
}
long KRTextureTGA::getMemRequiredForSize(int max_dim)
{
TGA_HEADER *pHeader = (TGA_HEADER *)m_pData->getStart();
switch(pHeader->imagetype) {
case 2: // rgb
switch(pHeader->bitsperpixel) {
case 24:
{
return pHeader->width * pHeader->height * 4;
}
break;
case 32:
{
return pHeader->width * pHeader->height * 4;
}
break;
}
break;
}
return 0;
}

View File

@@ -17,7 +17,9 @@ public:
KRTextureTGA(KRContext &context, KRDataBlock *data);
virtual ~KRTextureTGA();
bool uploadTexture(GLenum target, int lod_max_dim, int &current_lod_max_dim, size_t &textureMemUsed);
bool uploadTexture(GLenum target, int lod_max_dim, int &current_lod_max_dim, long &textureMemUsed);
virtual long getMemRequiredForSize(int max_dim);
};
#endif