More optimizations to reduce CPU utilization.

Fixed bug that caused cube maps to prematurely expire from the texture pool.

--HG--
extra : convert_revision : svn%3A7752d6cf-9f14-4ad2-affc-04f1e67b81a5/trunk%40161
This commit is contained in:
kearwood
2012-11-17 07:57:28 +00:00
parent aae50ff178
commit 5728b5b000
12 changed files with 109 additions and 57 deletions

View File

@@ -628,11 +628,14 @@
E491016E13C99BAE0098455B /* Classes */ = {
isa = PBXGroup;
children = (
E4F9753815362A5200FD60B2 /* 3rdparty */,
E488399915F92BA300BD66D5 /* Managers */,
E461A173152E59DF00F2044A /* Math */,
E461A170152E598200F2044A /* Resources */,
E4924C2415EE95E700B965C6 /* KROctree.cpp */,
E4924C2515EE95E800B965C6 /* KROctree.h */,
E4924C2915EE96AA00B965C6 /* KROctreeNode.cpp */,
E4924C2A15EE96AA00B965C6 /* KROctreeNode.h */,
E4F9753815362A5200FD60B2 /* 3rdparty */,
E48B3CBF14393E2F000C50E2 /* KRCamera.cpp */,
E48B3CBC14393DF5000C50E2 /* KRCamera.h */,
E48C697115374F7E00232E28 /* KRContext.cpp */,
@@ -646,9 +649,6 @@
E491016F13C99BDC0098455B /* KREngine.mm */,
E46F4A03155DF47C00CCF8B8 /* KRWorld.cpp */,
E46F49FF155DF46700CCF8B8 /* KRWorld.h */,
E488399915F92BA300BD66D5 /* Managers */,
E461A173152E59DF00F2044A /* Math */,
E461A170152E598200F2044A /* Resources */,
E4030E4B160A3CF000592648 /* KRStockGeometry.h */,
E4CA11731639CBD1005D9400 /* KRViewport.h */,
E4CA11771639CC8E005D9400 /* KRViewport.cpp */,

View File

@@ -207,10 +207,10 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
GLDEBUG(glDisable(GL_BLEND));
// Render the geometry
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_DEFERRED_GBUFFER);
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_DEFERRED_GBUFFER, true);
// ----====---- Generate Shadowmaps for Lights ----====----
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_GENERATE_SHADOWMAPS);
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_GENERATE_SHADOWMAPS, false);
GLDEBUG(glViewport(0, 0, m_viewport.getSize().x, m_viewport.getSize().y));
// ----====---- Opaque Geometry, Deferred rendering Pass 2 ----====----
@@ -237,7 +237,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
// Render the geometry
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_GENERATE_SHADOWMAPS);
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_GENERATE_SHADOWMAPS, false);
// ----====---- Opaque Geometry, Deferred rendering Pass 3 ----====----
// Set render target
@@ -269,7 +269,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
// Render the geometry
// TODO: At this point, we only want to render octree nodes that produced fragments during the 1st pass into the GBuffer
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_DEFERRED_OPAQUE);
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_DEFERRED_OPAQUE, false);
// Deactivate source buffer texture units
m_pContext->getTextureManager()->selectTexture(6, NULL);
@@ -280,7 +280,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
} else {
// ----====---- Generate Shadowmaps for Lights ----====----
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_GENERATE_SHADOWMAPS);
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_GENERATE_SHADOWMAPS, true);
GLDEBUG(glViewport(0, 0, m_viewport.getSize().x, m_viewport.getSize().y));
// ----====---- Opaque Geometry, Forward Rendering ----====----
@@ -311,7 +311,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
// Render the geometry
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_FORWARD_OPAQUE);
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_FORWARD_OPAQUE, false);
}
// ----====---- Sky Box ----====----
@@ -370,7 +370,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
GLDEBUG(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
// Render all transparent geometry
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_FORWARD_TRANSPARENT);
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_FORWARD_TRANSPARENT, false);
// ----====---- Flares ----====----
@@ -394,7 +394,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
GLDEBUG(glBlendFunc(GL_ONE, GL_ONE));
// Render all flares
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_ADDITIVE_PARTICLES);
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_ADDITIVE_PARTICLES, false);
// ----====---- Volumetric Lighting ----====----
@@ -422,7 +422,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
GLDEBUG(glDepthRangef(0.0, 1.0));
}
scene.render(this, m_viewport.getVisibleBounds(), volumetricLightingViewport, KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE);
scene.render(this, m_viewport.getVisibleBounds(), volumetricLightingViewport, KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE, false);
if(volumetric_environment_downsample != 0) {
// Set render target

View File

@@ -300,7 +300,7 @@ void KRLight::renderShadowBuffers(KRCamera *pCamera)
getContext().getShaderManager()->selectShader(shadowShader, m_shadowViewports[iShadow], KRMat4(), std::vector<KRLight *>(), KRNode::RENDER_PASS_SHADOWMAP);
getScene().render(pCamera, m_shadowViewports[iShadow].getVisibleBounds(), m_shadowViewports[iShadow], KRNode::RENDER_PASS_SHADOWMAP);
getScene().render(pCamera, m_shadowViewports[iShadow].getVisibleBounds(), m_shadowViewports[iShadow], KRNode::RENDER_PASS_SHADOWMAP, true);
}
}

View File

@@ -60,7 +60,22 @@ KRScene::~KRScene() {
#if TARGET_OS_IPHONE
void KRScene::render(KRCamera *pCamera, std::map<KRAABB, int> &visibleBounds, const KRViewport &viewport, KRNode::RenderPass renderPass) {
void KRScene::render(KRCamera *pCamera, std::map<KRAABB, int> &visibleBounds, const KRViewport &viewport, KRNode::RenderPass renderPass, bool new_frame) {
if(new_frame) {
// Expire cached occlusion test results.
// Cached "failed" results are expired on the next frame (marked with .second of -1)
// Cached "success" results are expired after KRENGINE_OCCLUSION_TEST_EXPIRY frames (marked with .second of the last frame
std::set<KRAABB> expired_visible_bounds;
for(std::map<KRAABB, int>::iterator visible_bounds_itr = visibleBounds.begin(); visible_bounds_itr != visibleBounds.end(); visible_bounds_itr++) {
if((*visible_bounds_itr).second == -1 || (*visible_bounds_itr).second + KRENGINE_OCCLUSION_TEST_EXPIRY < getContext().getCurrentFrame()) {
expired_visible_bounds.insert((*visible_bounds_itr).first);
}
}
for(std::set<KRAABB>::iterator expired_visible_bounds_itr = expired_visible_bounds.begin(); expired_visible_bounds_itr != expired_visible_bounds.end(); expired_visible_bounds_itr++) {
visibleBounds.erase(*expired_visible_bounds_itr);
}
}
std::vector<KRLight *> lights;
@@ -112,18 +127,6 @@ void KRScene::render(KRCamera *pCamera, std::map<KRAABB, int> &visibleBounds, co
for(std::vector<KROctreeNode *>::iterator octree_itr = remainingOctreesTestResultsOnly.begin(); octree_itr != remainingOctreesTestResultsOnly.end(); octree_itr++) {
render(*octree_itr, visibleBounds, pCamera, lights, viewport, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, true, true);
}
// Expire cached occlusion test results
std::set<KRAABB> expired_visible_bounds;
for(std::map<KRAABB, int>::iterator visible_bounds_itr = visibleBounds.begin(); visible_bounds_itr != visibleBounds.end(); visible_bounds_itr++) {
if((*visible_bounds_itr).second + KRENGINE_OCCLUSION_TEST_EXPIRY < getContext().getCurrentFrame()) {
expired_visible_bounds.insert((*visible_bounds_itr).first);
}
}
for(std::set<KRAABB>::iterator expired_visible_bounds_itr = expired_visible_bounds.begin(); expired_visible_bounds_itr != expired_visible_bounds.end(); expired_visible_bounds_itr++) {
visibleBounds.erase(*expired_visible_bounds_itr);
}
}
void KRScene::render(KROctreeNode *pOctreeNode, std::map<KRAABB, int> &visibleBounds, KRCamera *pCamera, std::vector<KRLight *> lights, const KRViewport &viewport, KRNode::RenderPass renderPass, std::vector<KROctreeNode *> &remainingOctrees, std::vector<KROctreeNode *> &remainingOctreesTestResults, std::vector<KROctreeNode *> &remainingOctreesTestResultsOnly, bool bOcclusionResultsPass, bool bOcclusionTestResultsOnly)
@@ -145,6 +148,9 @@ void KRScene::render(KROctreeNode *pOctreeNode, std::map<KRAABB, int> &visibleBo
// Schedule a pass to perform the rendering
remainingOctrees.push_back(pOctreeNode);
}
} else {
// Record -1 to indicate that the visibility test had failed
visibleBounds[octreeBounds] = -1;
}
GLDEBUG(glDeleteQueriesEXT(1, &pOctreeNode->m_occlusionQuery));
@@ -180,15 +186,20 @@ void KRScene::render(KROctreeNode *pOctreeNode, std::map<KRAABB, int> &visibleBo
// If the previous frame rendered this octree, then attempt to render it in this frame without performing a pre-occlusion test
std::map<KRAABB, int>::iterator match_itr = visibleBounds.find(octreeBounds);
if(match_itr != visibleBounds.end()) {
if((*match_itr).second == -1) {
// We have already tested these bounds with a negative result
bNeedOcclusionTest = false;
} else {
bVisible = true;
// We set bNeedOcclusionTest to false only when the previous occlusion test is old and we need to perform an occlusion test to record if this octree node was visible for the next frame
bNeedOcclusionTest = false;
}
}
}
if(!bVisible) {
if(!bVisible && bNeedOcclusionTest) {
// Optimization: If this is an empty octree node with only a single child node, then immediately try to render the child node without an occlusion test for this higher level, as it would be more expensive than the occlusion test for the child
if(pOctreeNode->getSceneNodes().empty()) {
int child_count = 0;

View File

@@ -62,7 +62,7 @@ public:
#if TARGET_OS_IPHONE
void render(KRCamera *pCamera, std::map<KRAABB, int> &visibleBounds, const KRViewport &viewport, KRNode::RenderPass renderPass);
void render(KRCamera *pCamera, std::map<KRAABB, int> &visibleBounds, const KRViewport &viewport, KRNode::RenderPass renderPass, bool new_frame);
void render(KROctreeNode *pOctreeNode, std::map<KRAABB, int> &visibleBounds, KRCamera *pCamera, std::vector<KRLight *> lights, const KRViewport &viewport, KRNode::RenderPass renderPass, std::vector<KROctreeNode *> &remainingOctrees, std::vector<KROctreeNode *> &remainingOctreesTestResults, std::vector<KROctreeNode *> &remainingOctreesTestResultsOnly, bool bOcclusionResultsPass, bool bOcclusionTestResultsOnly);

View File

@@ -43,8 +43,10 @@ void KRTexture::resize(int max_dim)
if(max_dim == 0) {
releaseHandle();
} else {
int requiredMemoryTransfer = getThroughputRequiredForResize(max_dim);
int requiredMemoryDelta = getMemRequiredForSize(max_dim) - getMemSize();
int target_dim = max_dim;
if(target_dim < m_min_lod_max_dim) target_dim = m_min_lod_max_dim;
int requiredMemoryTransfer = getThroughputRequiredForResize(target_dim);
int requiredMemoryDelta = getMemRequiredForSize(target_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)
@@ -59,11 +61,11 @@ void KRTexture::resize(int max_dim)
return;
}
if(m_current_lod_max_dim != max_dim || m_iHandle == 0) {
if(m_current_lod_max_dim != target_dim || m_iHandle == 0) {
releaseHandle();
}
if(m_iHandle == 0) {
if(!createGLTexture(max_dim)) {
if(!createGLTexture(target_dim)) {
assert(false);
}
}
@@ -74,21 +76,33 @@ void KRTexture::resize(int max_dim)
GLuint KRTexture::getHandle() {
if(m_iHandle == 0) {
resize(getContext().KRENGINE_MIN_TEXTURE_DIM);
//resize(getContext().KRENGINE_MIN_TEXTURE_DIM);
resize(m_min_lod_max_dim);
}
m_last_frame_used = getContext().getCurrentFrame();
resetPoolExpiry();
return m_iHandle;
}
void KRTexture::resetPoolExpiry()
{
m_last_frame_used = getContext().getCurrentFrame();
}
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);
if(max_dim == 0) {
return 0;
} else {
int target_dim = max_dim;
if(target_dim < m_min_lod_max_dim) target_dim = target_dim;
if(target_dim != m_current_lod_max_dim) {
int requiredMemory = getMemRequiredForSize(target_dim);
int requiredMemoryDelta = requiredMemory - getMemSize();
if(requiredMemoryDelta == 0) {
@@ -99,6 +113,7 @@ long KRTexture::getThroughputRequiredForResize(int max_dim)
} else {
return 0;
}
}
}
long KRTexture::getLastFrameUsed()

View File

@@ -55,6 +55,8 @@ public:
long getLastFrameUsed();
virtual void resetPoolExpiry();
protected:
virtual bool createGLTexture(int lod_max_dim) = 0;
GLuint getHandle();

View File

@@ -88,18 +88,33 @@ bool KRTextureCube::createGLTexture(int lod_max_dim)
long KRTextureCube::getMemRequiredForSize(int max_dim)
{
int target_dim = max_dim;
if(target_dim < m_min_lod_max_dim) target_dim = m_min_lod_max_dim;
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);
memoryRequired += faceTexture->getMemRequiredForSize(target_dim);
}
}
return memoryRequired;
}
void KRTextureCube::resetPoolExpiry()
{
KRTexture::resetPoolExpiry();
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) {
faceTexture->resetPoolExpiry(); // Ensure that side of cube maps do not expire from the texture pool prematurely, as they are referenced indirectly
}
}
}
void KRTextureCube::bind()
{
GLuint handle = getHandle();

View File

@@ -41,7 +41,7 @@ public:
virtual void bind();
virtual long getMemRequiredForSize(int max_dim);
virtual void resetPoolExpiry();
private:
virtual bool createGLTexture(int lod_max_dim);

View File

@@ -140,7 +140,11 @@ void KRTextureManager::startFrame()
void KRTextureManager::endFrame()
{
for(int iTexture=0; iTexture < KRENGINE_MAX_TEXTURE_UNITS; iTexture++) {
if(m_boundTextures[iTexture]) {
m_boundTextures[iTexture]->resetPoolExpiry(); // Even if the same texture is bound, ensure that they don't expire from the texture pool while in use
}
}
}
void KRTextureManager::balanceTextureMemory()

View File

@@ -152,6 +152,9 @@ KRTexturePVR::~KRTexturePVR() {
long KRTexturePVR::getMemRequiredForSize(int max_dim)
{
int target_dim = max_dim;
if(target_dim < m_min_lod_max_dim) target_dim = target_dim;
// Determine how much memory will be consumed
int width = m_iWidth;
int height = m_iHeight;
@@ -159,7 +162,7 @@ long KRTexturePVR::getMemRequiredForSize(int max_dim)
for(std::list<dataBlockStruct>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
dataBlockStruct block = *itr;
if(width <= max_dim && height <= max_dim) {
if(width <= target_dim && height <= target_dim) {
memoryRequired += block.length;
}
@@ -178,6 +181,8 @@ long KRTexturePVR::getMemRequiredForSize(int max_dim)
bool KRTexturePVR::uploadTexture(GLenum target, int lod_max_dim, int &current_lod_max_dim, long &textureMemUsed)
{
int target_dim = lod_max_dim;
if(target_dim < m_min_lod_max_dim) target_dim = target_dim;
GLenum err;
@@ -195,7 +200,7 @@ bool KRTexturePVR::uploadTexture(GLenum target, int lod_max_dim, int &current_lo
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) {
if(width <= target_dim && height <= target_dim) {
memoryRequired += block.length;
if(width > current_lod_max_dim) {
current_lod_max_dim = width;