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 */, E491018013C99BDC0098455B /* KRTextureManager.cpp */,
E491018613C99BDC0098455B /* KRTexture2D.h */, E491018613C99BDC0098455B /* KRTexture2D.h */,
E491018113C99BDC0098455B /* KRTexture2D.cpp */, E491018113C99BDC0098455B /* KRTexture2D.cpp */,
E4B175AA161F5A1000B8FB80 /* KRTexture.cpp */,
E4B175AB161F5A1000B8FB80 /* KRTexture.h */, E4B175AB161F5A1000B8FB80 /* KRTexture.h */,
E4B175B0161F5FAE00B8FB80 /* KRTextureCube.cpp */, E4B175AA161F5A1000B8FB80 /* KRTexture.cpp */,
E4B175B1161F5FAF00B8FB80 /* KRTextureCube.h */, E4B175B1161F5FAF00B8FB80 /* KRTextureCube.h */,
E4B175B0161F5FAE00B8FB80 /* KRTextureCube.cpp */,
E4CA10E41637BD0A005D9400 /* KRTexturePVR.h */, E4CA10E41637BD0A005D9400 /* KRTexturePVR.h */,
E4CA10E81637BD2B005D9400 /* KRTexturePVR.cpp */, E4CA10E81637BD2B005D9400 /* KRTexturePVR.cpp */,
E4CA10EB1637BD47005D9400 /* KRTextureTGA.h */, E4CA10EB1637BD47005D9400 /* KRTextureTGA.h */,

View File

@@ -164,8 +164,6 @@ void KRCamera::renderFrame(KRScene &scene, KRMat4 &viewMatrix, float deltaTime)
setViewportSize(KRVector2(backingWidth, backingHeight)); setViewportSize(KRVector2(backingWidth, backingHeight));
m_viewport = KRViewport(getViewportSize(), viewMatrix, getProjectionMatrix()); m_viewport = KRViewport(getViewportSize(), viewMatrix, getProjectionMatrix());
m_pContext->rotateBuffers(true);
renderFrame(scene, deltaTime); renderFrame(scene, deltaTime);
GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO)); GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO));
@@ -232,10 +230,10 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
GLDEBUG(glDepthMask(GL_FALSE)); GLDEBUG(glDepthMask(GL_FALSE));
// Set source to buffers from pass 1 // 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(glActiveTexture(GL_TEXTURE6));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeColorTexture)); GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeColorTexture));
m_pContext->getTextureManager()->selectTexture(7, NULL, 0); m_pContext->getTextureManager()->selectTexture(7, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE7)); GLDEBUG(glActiveTexture(GL_TEXTURE7));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeDepthTexture)); GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeDepthTexture));
@@ -255,7 +253,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
GLDEBUG(glClear(GL_COLOR_BUFFER_BIT)); GLDEBUG(glClear(GL_COLOR_BUFFER_BIT));
// Set source to buffers from pass 2 // 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(glActiveTexture(GL_TEXTURE6));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, lightAccumulationTexture)); 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); scene.render(this, emptyBoundsSet, m_viewport, KRNode::RENDER_PASS_DEFERRED_OPAQUE, newVisibleBounds);
// Deactivate source buffer texture units // Deactivate source buffer texture units
m_pContext->getTextureManager()->selectTexture(6, NULL, 0); m_pContext->getTextureManager()->selectTexture(6, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE6)); GLDEBUG(glActiveTexture(GL_TEXTURE6));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0)); GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
m_pContext->getTextureManager()->selectTexture(7, NULL, 0); m_pContext->getTextureManager()->selectTexture(7, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE7)); GLDEBUG(glActiveTexture(GL_TEXTURE7));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0)); GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
} else { } else {
@@ -342,7 +340,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
if(m_pSkyBoxTexture) { 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().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 // Render a full screen quad
m_pContext->getModelManager()->bindVBO((void *)KRENGINE_VBO_2D_SQUARE, KRENGINE_VBO_2D_SQUARE_SIZE, true, false, false, true, false); 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 // Disable z-buffer test
GLDEBUG(glDisable(GL_DEPTH_TEST)); GLDEBUG(glDisable(GL_DEPTH_TEST));
m_pContext->getTextureManager()->selectTexture(0, NULL, 0); m_pContext->getTextureManager()->selectTexture(0, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE0)); GLDEBUG(glActiveTexture(GL_TEXTURE0));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeDepthTexture)); 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); 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); 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(glActiveTexture(GL_TEXTURE0));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeDepthTexture)); GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeDepthTexture));
m_pContext->getTextureManager()->selectTexture(1, NULL, 0); m_pContext->getTextureManager()->selectTexture(1, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE1)); GLDEBUG(glActiveTexture(GL_TEXTURE1));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeColorTexture)); GLDEBUG(glBindTexture(GL_TEXTURE_2D, compositeColorTexture));
if(volumetric_environment_enable) { if(volumetric_environment_enable) {
m_pContext->getTextureManager()->selectTexture(2, NULL, 0); m_pContext->getTextureManager()->selectTexture(2, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE2)); GLDEBUG(glActiveTexture(GL_TEXTURE2));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, volumetricLightAccumulationTexture)); GLDEBUG(glBindTexture(GL_TEXTURE_2D, volumetricLightAccumulationTexture));
} }
@@ -684,11 +682,11 @@ void KRCamera::renderPost()
GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); 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(glActiveTexture(GL_TEXTURE0));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0)); GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
m_pContext->getTextureManager()->selectTexture(1, NULL, 0); m_pContext->getTextureManager()->selectTexture(1, NULL);
GLDEBUG(glActiveTexture(GL_TEXTURE1)); GLDEBUG(glActiveTexture(GL_TEXTURE1));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0)); GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
@@ -701,7 +699,7 @@ void KRCamera::renderPost()
// viewMatrix.scale(0.20, 0.20, 0.20); // viewMatrix.scale(0.20, 0.20, 0.20);
// viewMatrix.translate(-0.70, 0.70 - 0.45 * iShadow, 0.0); // 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); // 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); // m_pContext->getModelManager()->bindVBO((void *)KRENGINE_VBO_2D_SQUARE, KRENGINE_VBO_2D_SQUARE_SIZE, true, false, false, true, false);
// GLDEBUG(glActiveTexture(GL_TEXTURE0)); // GLDEBUG(glActiveTexture(GL_TEXTURE0));
// GLDEBUG(glBindTexture(GL_TEXTURE_2D, shadowDepthTexture[iShadow])); // GLDEBUG(glBindTexture(GL_TEXTURE_2D, shadowDepthTexture[iShadow]));
@@ -714,7 +712,7 @@ void KRCamera::renderPost()
//#endif //#endif
// } // }
// //
// m_pContext->getTextureManager()->selectTexture(0, NULL, 0); // m_pContext->getTextureManager()->selectTexture(0, NULL);
// GLDEBUG(glActiveTexture(GL_TEXTURE0)); // GLDEBUG(glActiveTexture(GL_TEXTURE0));
// GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0)); // GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
// } // }
@@ -725,7 +723,7 @@ void KRCamera::renderPost()
if(*szText) { 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); 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; const char *pChar = szText;
int iPos=0; int iPos=0;
@@ -763,7 +761,7 @@ void KRCamera::renderPost()
GLDEBUG(glActiveTexture(GL_TEXTURE0)); GLDEBUG(glActiveTexture(GL_TEXTURE0));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0)); 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_TARGET_TEXTURE_MEM_MIN;
int KRContext::KRENGINE_MAX_TEXTURE_DIM; int KRContext::KRENGINE_MAX_TEXTURE_DIM;
int KRContext::KRENGINE_MIN_TEXTURE_DIM; int KRContext::KRENGINE_MIN_TEXTURE_DIM;
int KRContext::KRENGINE_MAX_TEXTURE_THROUGHPUT;
const char *KRContext::extension_names[KRENGINE_NUM_EXTENSIONS] = { const char *KRContext::extension_names[KRENGINE_NUM_EXTENSIONS] = {
"GL_EXT_texture_storage" "GL_EXT_texture_storage"
@@ -33,6 +34,7 @@ KRContext::KRContext() {
m_pModelManager = new KRModelManager(*this); m_pModelManager = new KRModelManager(*this);
m_pSceneManager = new KRSceneManager(*this); m_pSceneManager = new KRSceneManager(*this);
m_bDetectedExtensions = false; m_bDetectedExtensions = false;
m_current_frame = 0;
} }
KRContext::~KRContext() { KRContext::~KRContext() {
@@ -130,13 +132,29 @@ void KRContext::loadResource(std::string path) {
void KRContext::rotateBuffers(bool new_frame) { void KRContext::rotateBuffers(bool new_frame) {
//fprintf(stderr, "Rotating Buffers...\n"); //fprintf(stderr, "Rotating Buffers...\n");
GLDEBUG(glFinish()); if(!new_frame) GLDEBUG(glFinish());
m_pModelManager->rotateBuffers(new_frame); m_pModelManager->rotateBuffers(new_frame);
m_pTextureManager->rotateBuffers(new_frame);
} }
void KRContext::detectExtensions() { void KRContext::detectExtensions() {
m_bDetectedExtensions = true; 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_TARGET_TEXTURE_MEM_MIN;
static int KRENGINE_MAX_TEXTURE_DIM; static int KRENGINE_MAX_TEXTURE_DIM;
static int KRENGINE_MIN_TEXTURE_DIM; static int KRENGINE_MIN_TEXTURE_DIM;
static int KRENGINE_MAX_TEXTURE_THROUGHPUT;
KRContext(); KRContext();
@@ -55,6 +56,11 @@ public:
static const char * extension_names[KRENGINE_NUM_EXTENSIONS]; static const char * extension_names[KRENGINE_NUM_EXTENSIONS];
static bool extension_available[KRENGINE_NUM_EXTENSIONS]; static bool extension_available[KRENGINE_NUM_EXTENSIONS];
void startFrame();
void endFrame();
long getCurrentFrame();
private: private:
KRBundleManager *m_pBundleManager; KRBundleManager *m_pBundleManager;
KRSceneManager *m_pSceneManager; KRSceneManager *m_pSceneManager;
@@ -65,6 +71,8 @@ private:
void detectExtensions(); void detectExtensions();
bool m_bDetectedExtensions; bool m_bDetectedExtensions;
long m_current_frame;
}; };
#endif #endif

View File

@@ -79,6 +79,7 @@ float const PI = 3.141592653589793f;
KRContext::KRENGINE_TARGET_TEXTURE_MEM_MIN = 32000000 * 2; KRContext::KRENGINE_TARGET_TEXTURE_MEM_MIN = 32000000 * 2;
KRContext::KRENGINE_MAX_TEXTURE_DIM = 2048; KRContext::KRENGINE_MAX_TEXTURE_DIM = 2048;
KRContext::KRENGINE_MIN_TEXTURE_DIM = 64; KRContext::KRENGINE_MIN_TEXTURE_DIM = 64;
KRContext::KRENGINE_MAX_TEXTURE_THROUGHPUT = 32000000;
} else { } else {
KRContext::KRENGINE_MAX_VBO_HANDLES = 10000; KRContext::KRENGINE_MAX_VBO_HANDLES = 10000;
KRContext::KRENGINE_MAX_VBO_MEM = 128000000; KRContext::KRENGINE_MAX_VBO_MEM = 128000000;
@@ -89,6 +90,7 @@ float const PI = 3.141592653589793f;
KRContext::KRENGINE_TARGET_TEXTURE_MEM_MIN = 32000000; KRContext::KRENGINE_TARGET_TEXTURE_MEM_MIN = 32000000;
KRContext::KRENGINE_MAX_TEXTURE_DIM = 2048; KRContext::KRENGINE_MAX_TEXTURE_DIM = 2048;
KRContext::KRENGINE_MIN_TEXTURE_DIM = 64; KRContext::KRENGINE_MIN_TEXTURE_DIM = 64;
KRContext::KRENGINE_MAX_TEXTURE_THROUGHPUT = 32000000;
} }
_camera = NULL; _camera = NULL;
@@ -158,7 +160,9 @@ float const PI = 3.141592653589793f;
- (void)renderScene: (KRScene *)pScene WithViewMatrix: (KRMat4)viewMatrix AndDeltaTime: (float)deltaTime - (void)renderScene: (KRScene *)pScene WithViewMatrix: (KRMat4)viewMatrix AndDeltaTime: (float)deltaTime
{ {
_context->startFrame();
_camera->renderFrame(*pScene, viewMatrix, deltaTime); _camera->renderFrame(*pScene, viewMatrix, deltaTime);
_context->endFrame();
} }
- (BOOL)loadShaders - (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) { 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); 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], pShader->m_uniforms[KRShader::KRENGINE_UNIFORM_FLARE_SIZE],
m_flareSize 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); 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)); 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); glViewport(0, 0, m_shadowViewports[iShadow].getSize().x, m_shadowViewports[iShadow].getSize().y);
GLDEBUG(glBindFramebuffer(GL_FRAMEBUFFER, shadowFramebuffer[iShadow])); 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(glClearDepthf(1.0f));
GLDEBUG(glClear(GL_DEPTH_BUFFER_BIT)); 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)); GLDEBUG(glUniform1f(pShader->m_uniforms[KRShader::KRENGINE_UNIFORM_MATERIAL_ALPHA], m_tr));
if(bDiffuseMap) { if(bDiffuseMap) {
m_pContext->getTextureManager()->selectTexture(0, m_pDiffuseMap, 2048); m_pContext->getTextureManager()->selectTexture(0, m_pDiffuseMap);
} }
if(bSpecMap) { if(bSpecMap) {
m_pContext->getTextureManager()->selectTexture(1, m_pSpecularMap, 2048); m_pContext->getTextureManager()->selectTexture(1, m_pSpecularMap);
} }
if(bNormalMap) { 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)) { 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)) { 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 // 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; *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); configureAttribs(enable_vertex, enable_normal, enable_tangent, enable_uva, enable_uvb);
#endif #endif
} else { } 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()) { if(m_vbosPool.empty()) {
fprintf(stderr, "flushBuffers due to VBO exhaustion...\n"); fprintf(stderr, "flushBuffers due to VBO exhaustion...\n");
m_pContext->rotateBuffers(false); 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(glBindBuffer(GL_ARRAY_BUFFER, m_currentVBO.vbo_handle));
GLDEBUG(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW)); GLDEBUG(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW));
m_vboMemUsed += size;
configureAttribs(enable_vertex, enable_normal, enable_tangent, enable_uva, enable_uvb); configureAttribs(enable_vertex, enable_normal, enable_tangent, enable_uva, enable_uvb);
m_currentVBO.size = size; m_currentVBO.size = size;

View File

@@ -67,7 +67,7 @@ void KRParticleSystemBrownian::render(KRCamera *pCamera, std::vector<KRLight *>
GLDEBUG(glDepthRangef(0.0, 1.0)); GLDEBUG(glDepthRangef(0.0, 1.0));
KRTexture *pParticleTexture = m_pContext->getTextureManager()->getTexture("flare"); KRTexture *pParticleTexture = m_pContext->getTextureManager()->getTexture("flare");
m_pContext->getTextureManager()->selectTexture(0, pParticleTexture, 2048); m_pContext->getTextureManager()->selectTexture(0, pParticleTexture);
int particle_count = 10000; 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) { if(light_directional_count == 0) {
int cShadowBuffers = directional_light->getShadowBufferCount(); int cShadowBuffers = directional_light->getShadowBufferCount();
if(m_uniforms[KRENGINE_UNIFORM_SHADOWTEXTURE1] != -1 && cShadowBuffers > 0) { 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(glActiveTexture(GL_TEXTURE3));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, directional_light->getShadowTextures()[0])); GLDEBUG(glBindTexture(GL_TEXTURE_2D, directional_light->getShadowTextures()[0]));
GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); 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) { 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(glActiveTexture(GL_TEXTURE4));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, directional_light->getShadowTextures()[1])); GLDEBUG(glBindTexture(GL_TEXTURE_2D, directional_light->getShadowTextures()[1]));
GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); 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) { 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(glActiveTexture(GL_TEXTURE5));
GLDEBUG(glBindTexture(GL_TEXTURE_2D, directional_light->getShadowTextures()[2])); GLDEBUG(glBindTexture(GL_TEXTURE_2D, directional_light->getShadowTextures()[2]));
GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); 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. // Copyright (c) 2012 Kearwood Software. All rights reserved.
// //
#include <assert.h>
#include "KRTexture.h" #include "KRTexture.h"
#include "KRDataBlock.h" #include "KRDataBlock.h"
#include <assert.h> #include "KRContext.h"
#include "KRTextureManager.h"
KRTexture::KRTexture(KRContext &context) : KRContextObject(context) KRTexture::KRTexture(KRContext &context) : KRContextObject(context)
{ {
m_iHandle = 0; m_iHandle = 0;
m_textureMemUsed = 0; m_textureMemUsed = 0;
m_last_frame_used = 0;
} }
KRTexture::~KRTexture() KRTexture::~KRTexture()
{ {
size_t textureMemFreed = 0; releaseHandle();
releaseHandle(textureMemFreed);
} }
void KRTexture::releaseHandle(size_t &textureMemUsed) { void KRTexture::releaseHandle() {
textureMemUsed -= getMemSize();
if(m_iHandle != 0) { if(m_iHandle != 0) {
GLDEBUG(glDeleteTextures(1, &m_iHandle)); GLDEBUG(glDeleteTextures(1, &m_iHandle));
getContext().getTextureManager()->memoryChanged(-getMemSize());
m_iHandle = 0; m_iHandle = 0;
m_textureMemUsed = 0; m_textureMemUsed = 0;
} }
textureMemUsed += getMemSize();
} }
long KRTexture::getMemSize() { long KRTexture::getMemSize() {
return m_textureMemUsed; // TODO - This is not 100% accurate, as loaded format may differ in size while in GPU memory 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) { void KRTexture::resize(int max_dim)
// Constrain target LOD to be within mipmap levels of texture {
int target_dim = max_dim; if(max_dim == 0) {
releaseHandle();
} else {
int requiredMemoryTransfer = getThroughputRequiredForResize(max_dim);
int requiredMemoryDelta = getMemRequiredForSize(max_dim) - getMemSize();
// ---- Start: incremental texture loading ---- if(requiredMemoryDelta) {
if(m_current_lod_max_dim == 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)
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(getContext().getTextureManager()->getMemoryTransferedThisFrame() + requiredMemoryTransfer > getContext().KRENGINE_MAX_TEXTURE_THROUGHPUT) {
if(target_dim > m_max_lod_max_dim) target_dim = m_max_lod_max_dim; // Exceeding per-frame transfer throughput; can't resize now
return;
}
if(can_resize && m_current_lod_max_dim != target_dim) { if(getContext().getTextureManager()->getMemUsed() + requiredMemoryDelta > getContext().KRENGINE_MAX_TEXTURE_MEM) {
size_t memFreed = 0; // Exceeding total memory allocated to textures; can't resize now
releaseHandle(memFreed); return;
} }
if(m_iHandle == 0) {
if(!createGLTexture(target_dim)) { if(m_current_lod_max_dim != max_dim || m_iHandle == 0) {
assert(false); 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; 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); KRTexture(KRContext &context);
virtual ~KRTexture(); virtual ~KRTexture();
virtual void bind(size_t &textureMemUsed, int max_dim, bool can_resize) = 0; virtual void bind() = 0;
void releaseHandle(size_t &textureMemUsed); void releaseHandle();
long getMemSize(); long getMemSize();
virtual long getMemRequiredForSize(int max_dim) = 0;
virtual long getThroughputRequiredForResize(int max_dim);
virtual void resize(int max_dim);
long getLastFrameUsed();
protected: protected:
virtual bool createGLTexture(int lod_max_dim) = 0; virtual bool createGLTexture(int lod_max_dim) = 0;
GLuint getHandle(int max_dim, bool can_resize); GLuint getHandle();
GLuint m_iHandle; GLuint m_iHandle;
size_t m_textureMemUsed; long m_textureMemUsed;
int m_current_lod_max_dim; int m_current_lod_max_dim;
uint32_t m_max_lod_max_dim; uint32_t m_max_lod_max_dim;
uint32_t m_min_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 { } else {
GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); 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)) { if(!uploadTexture(GL_TEXTURE_2D, lod_max_dim, m_current_lod_max_dim, m_textureMemUsed)) {
GLDEBUG(glDeleteTextures(1, &m_iHandle)); GLDEBUG(glDeleteTextures(1, &m_iHandle));
m_iHandle = 0; m_iHandle = 0;
@@ -70,19 +71,20 @@ bool KRTexture2D::createGLTexture(int lod_max_dim) {
return false; return false;
} }
return true; return true;
} }
void KRTexture2D::bind(size_t &textureMemUsed, int max_dim, bool can_resize) { void KRTexture2D::bind() {
textureMemUsed -= getMemSize(); GLuint handle = getHandle();
GLDEBUG(glBindTexture(GL_TEXTURE_2D, getHandle(max_dim, can_resize)));
// TODO - These texture parameters should be assigned by the material or texture parameters GLDEBUG(glBindTexture(GL_TEXTURE_2D, handle));
GLDEBUG(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f)); if(handle) {
GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)); // TODO - These texture parameters should be assigned by the material or texture parameters
GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)); GLDEBUG(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f));
GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT));
textureMemUsed += getMemSize(); GLDEBUG(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT));
}
} }
int KRTexture2D::getMaxMipMap() { int KRTexture2D::getMaxMipMap() {
@@ -96,4 +98,3 @@ int KRTexture2D::getMinMipMap() {
bool KRTexture2D::hasMipmaps() { bool KRTexture2D::hasMipmaps() {
return m_max_lod_max_dim != m_min_lod_max_dim; return m_max_lod_max_dim != m_min_lod_max_dim;
} }

View File

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

View File

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

View File

@@ -40,11 +40,10 @@
KRTextureManager::KRTextureManager(KRContext &context) : KRContextObject(context) { KRTextureManager::KRTextureManager(KRContext &context) : KRContextObject(context) {
m_textureMemUsed = 0; m_textureMemUsed = 0;
m_activeTextureMemUsed = 0;
m_lod_max_dim_cap = 2048;
for(int iTexture=0; iTexture<KRENGINE_MAX_TEXTURE_UNITS; iTexture++) { for(int iTexture=0; iTexture<KRENGINE_MAX_TEXTURE_UNITS; iTexture++) {
m_boundTextures[iTexture] = NULL; m_boundTextures[iTexture] = NULL;
} }
m_memoryTransferredThisFrame = 0;
} }
KRTextureManager::~KRTextureManager() { 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) { if(m_boundTextures[iTextureUnit] != pTexture) {
GLDEBUG(glActiveTexture(GL_TEXTURE0 + iTextureUnit)); GLDEBUG(glActiveTexture(GL_TEXTURE0 + iTextureUnit));
if(pTexture != NULL) { if(pTexture != NULL) {
m_poolTextures.erase(pTexture); m_poolTextures.erase(pTexture);
bool bActive = true;
if(m_activeTextures.find(pTexture) == m_activeTextures.end()) { if(m_activeTextures.find(pTexture) == m_activeTextures.end()) {
bActive = false;
m_activeTextures.insert(pTexture); m_activeTextures.insert(pTexture);
} }
size_t textureMemChange = 0; pTexture->bind();
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();
}
} else { } else {
GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0)); GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
} }
m_boundTextures[iTextureUnit] = pTexture; 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; return m_textureMemUsed;
} }
size_t KRTextureManager::getActiveMemUsed() { void KRTextureManager::startFrame()
return m_activeTextureMemUsed; {
m_memoryTransferredThisFrame = 0;
balanceTextureMemory();
rotateBuffers();
} }
void KRTextureManager::endFrame()
void KRTextureManager::rotateBuffers(bool new_frame)
{ {
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;
for(int iTexture=0; iTexture < KRENGINE_MAX_TEXTURE_UNITS; iTexture++) { }
KRTexture *pBoundTexture = m_boundTextures[iTexture];
if(pBoundTexture != NULL) { void KRTextureManager::balanceTextureMemory()
m_poolTextures.erase(pBoundTexture); {
if(m_activeTextures.find(pBoundTexture) == m_activeTextures.end()) { // Balance texture memory by reducing and increasing the maximum mip-map level of both active and inactive textures
m_activeTextures.insert(pBoundTexture); // Favour performance over maximum texture resolution when memory is insufficient for textures at full resolution.
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() // Strip off mipmap levels of inactive textures to free up memory
{ long inactive_texture_mem_used_target = 0;
if(m_lod_max_dim_cap > KRContext::KRENGINE_MIN_TEXTURE_DIM) { for(std::set<KRTexture *>::iterator itr=m_poolTextures.begin(); itr != m_poolTextures.end(); itr++) {
m_lod_max_dim_cap = m_lod_max_dim_cap >> 1; 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() // Determine the maximum mipmap level for the active textures we can achieve with the memory that is available
{ long memory_available = 0;
if(m_lod_max_dim_cap < KRContext::KRENGINE_MAX_TEXTURE_DIM) { long maxDimActive = getContext().KRENGINE_MAX_TEXTURE_DIM;
m_lod_max_dim_cap = m_lod_max_dim_cap << 1; 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); KRTextureManager(KRContext &context);
virtual ~KRTextureManager(); virtual ~KRTextureManager();
void rotateBuffers(bool new_frame); void selectTexture(int iTextureUnit, KRTexture *pTexture);
void selectTexture(int iTextureUnit, KRTexture *pTexture, int lod_max_dim);
KRTexture *loadTexture(const char *szName, const char *szExtension, KRDataBlock *data); KRTexture *loadTexture(const char *szName, const char *szExtension, KRDataBlock *data);
KRTexture *getTextureCube(const char *szName); KRTexture *getTextureCube(const char *szName);
KRTexture *getTexture(const char *szFile); KRTexture *getTexture(const char *szFile);
size_t getMemUsed(); long getMemUsed();
size_t getActiveMemUsed();
int getLODDimCap(); long getMemoryTransferedThisFrame();
void addMemoryTransferredThisFrame(long memoryTransferred);
void memoryChanged(long memoryDelta);
void startFrame();
void endFrame();
private: private:
void decreaseLODCap(); long m_memoryTransferredThisFrame;
void increaseLODCap();
std::map<std::string, KRTexture *> m_textures; std::map<std::string, KRTexture *> m_textures;
@@ -72,10 +74,11 @@ private:
std::set<KRTexture *> m_activeTextures; std::set<KRTexture *> m_activeTextures;
std::set<KRTexture *> m_poolTextures; std::set<KRTexture *> m_poolTextures;
size_t m_textureMemUsed; long m_textureMemUsed;
size_t m_activeTextureMemUsed;
int m_lod_max_dim_cap;
void rotateBuffers();
void balanceTextureMemory();
}; };
#endif #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 width = m_iWidth;
int height = m_iHeight; 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; GLenum err;
if(m_blocks.size() == 0) { if(m_blocks.size() == 0) {
return false; 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; int i=0;
for(std::list<dataBlockStruct>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) { for(std::list<dataBlockStruct>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
dataBlockStruct block = *itr; dataBlockStruct block = *itr;
if(width <= lod_max_dim && height <= lod_max_dim) { if(width <= lod_max_dim && height <= lod_max_dim) {
memoryRequired += block.length;
if(width > current_lod_max_dim) { if(width > current_lod_max_dim) {
current_lod_max_dim = width; 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); glCompressedTexImage2D(target, i, m_internalFormat, width, height, 0, block.length, block.start);
err = glGetError(); err = glGetError();
if (err != GL_NO_ERROR) { if (err != GL_NO_ERROR) {
assert(false);
return false; return false;
} }
textureMemUsed += block.length;
i++; 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; return true;
} }

View File

@@ -17,7 +17,9 @@ public:
KRTexturePVR(KRContext &context, KRDataBlock *data); KRTexturePVR(KRContext &context, KRDataBlock *data);
virtual ~KRTexturePVR(); 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: protected:

View File

@@ -8,6 +8,7 @@
#include "KRTextureTGA.h" #include "KRTextureTGA.h"
#include "KREngine-common.h" #include "KREngine-common.h"
#include "KRContext.h"
typedef struct { typedef struct {
char idlength; 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(); 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) { if(pHeader->colourmaptype != 0) {
return false; // Mapped colors not supported 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); glTexImage2D(target, 0, GL_RGBA, pHeader->width, pHeader->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid *)converted_image);
delete converted_image; delete converted_image;
err = glGetError(); err = glGetError();
if (err != GL_NO_ERROR) return false; if (err != GL_NO_ERROR) {
textureMemUsed += pHeader->width * pHeader->height * 4; 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; current_lod_max_dim = m_max_lod_max_dim;
} }
break; 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); glTexImage2D(target, 0, GL_RGBA, pHeader->width, pHeader->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid *)pData);
err = glGetError(); err = glGetError();
if (err != GL_NO_ERROR) return false; if (err != GL_NO_ERROR) {
textureMemUsed += pHeader->width * pHeader->height * 4; 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; current_lod_max_dim = m_max_lod_max_dim;
} }
break; break;
@@ -98,3 +109,26 @@ bool KRTextureTGA::uploadTexture(GLenum target, int lod_max_dim, int &current_lo
return true; 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); KRTextureTGA(KRContext &context, KRDataBlock *data);
virtual ~KRTextureTGA(); 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 #endif