From 5728b5b000e9c2f007a8e74be40eca6ae7b62e4d Mon Sep 17 00:00:00 2001 From: kearwood Date: Sat, 17 Nov 2012 07:57:28 +0000 Subject: [PATCH] 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 --- KREngine/KREngine.xcodeproj/project.pbxproj | 8 ++-- KREngine/KREngine/Classes/KRCamera.cpp | 18 +++---- KREngine/KREngine/Classes/KRLight.cpp | 2 +- KREngine/KREngine/Classes/KRScene.cpp | 47 ++++++++++++------- KREngine/KREngine/Classes/KRScene.h | 2 +- KREngine/KREngine/Classes/KRTexture.cpp | 47 ++++++++++++------- KREngine/KREngine/Classes/KRTexture.h | 2 + KREngine/KREngine/Classes/KRTexture2D.cpp | 2 +- KREngine/KREngine/Classes/KRTextureCube.cpp | 17 ++++++- KREngine/KREngine/Classes/KRTextureCube.h | 2 +- .../KREngine/Classes/KRTextureManager.cpp | 6 ++- KREngine/KREngine/Classes/KRTexturePVR.cpp | 13 +++-- 12 files changed, 109 insertions(+), 57 deletions(-) diff --git a/KREngine/KREngine.xcodeproj/project.pbxproj b/KREngine/KREngine.xcodeproj/project.pbxproj index fe9c786..86ef886 100644 --- a/KREngine/KREngine.xcodeproj/project.pbxproj +++ b/KREngine/KREngine.xcodeproj/project.pbxproj @@ -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 */, diff --git a/KREngine/KREngine/Classes/KRCamera.cpp b/KREngine/KREngine/Classes/KRCamera.cpp index 5374564..bb87151 100644 --- a/KREngine/KREngine/Classes/KRCamera.cpp +++ b/KREngine/KREngine/Classes/KRCamera.cpp @@ -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 diff --git a/KREngine/KREngine/Classes/KRLight.cpp b/KREngine/KREngine/Classes/KRLight.cpp index ee891f5..b09d811 100644 --- a/KREngine/KREngine/Classes/KRLight.cpp +++ b/KREngine/KREngine/Classes/KRLight.cpp @@ -300,7 +300,7 @@ void KRLight::renderShadowBuffers(KRCamera *pCamera) getContext().getShaderManager()->selectShader(shadowShader, m_shadowViewports[iShadow], KRMat4(), std::vector(), 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); } } diff --git a/KREngine/KREngine/Classes/KRScene.cpp b/KREngine/KREngine/Classes/KRScene.cpp index 073a8c3..e74d842 100644 --- a/KREngine/KREngine/Classes/KRScene.cpp +++ b/KREngine/KREngine/Classes/KRScene.cpp @@ -60,7 +60,22 @@ KRScene::~KRScene() { #if TARGET_OS_IPHONE -void KRScene::render(KRCamera *pCamera, std::map &visibleBounds, const KRViewport &viewport, KRNode::RenderPass renderPass) { +void KRScene::render(KRCamera *pCamera, std::map &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 expired_visible_bounds; + for(std::map::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::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 lights; @@ -112,18 +127,6 @@ void KRScene::render(KRCamera *pCamera, std::map &visibleBounds, co for(std::vector::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 expired_visible_bounds; - for(std::map::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::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 &visibleBounds, KRCamera *pCamera, std::vector lights, const KRViewport &viewport, KRNode::RenderPass renderPass, std::vector &remainingOctrees, std::vector &remainingOctreesTestResults, std::vector &remainingOctreesTestResultsOnly, bool bOcclusionResultsPass, bool bOcclusionTestResultsOnly) @@ -145,6 +148,9 @@ void KRScene::render(KROctreeNode *pOctreeNode, std::map &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 &visibleBo // If the previous frame rendered this octree, then attempt to render it in this frame without performing a pre-occlusion test std::map::iterator match_itr = visibleBounds.find(octreeBounds); if(match_itr != visibleBounds.end()) { - 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((*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; diff --git a/KREngine/KREngine/Classes/KRScene.h b/KREngine/KREngine/Classes/KRScene.h index 29b1d1a..58b7563 100644 --- a/KREngine/KREngine/Classes/KRScene.h +++ b/KREngine/KREngine/Classes/KRScene.h @@ -62,7 +62,7 @@ public: #if TARGET_OS_IPHONE - void render(KRCamera *pCamera, std::map &visibleBounds, const KRViewport &viewport, KRNode::RenderPass renderPass); + void render(KRCamera *pCamera, std::map &visibleBounds, const KRViewport &viewport, KRNode::RenderPass renderPass, bool new_frame); void render(KROctreeNode *pOctreeNode, std::map &visibleBounds, KRCamera *pCamera, std::vector lights, const KRViewport &viewport, KRNode::RenderPass renderPass, std::vector &remainingOctrees, std::vector &remainingOctreesTestResults, std::vector &remainingOctreesTestResultsOnly, bool bOcclusionResultsPass, bool bOcclusionTestResultsOnly); diff --git a/KREngine/KREngine/Classes/KRTexture.cpp b/KREngine/KREngine/Classes/KRTexture.cpp index f6f76cc..8162776 100644 --- a/KREngine/KREngine/Classes/KRTexture.cpp +++ b/KREngine/KREngine/Classes/KRTexture.cpp @@ -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,30 +76,43 @@ 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); - int requiredMemoryDelta = requiredMemory - getMemSize(); + + 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(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) + + if(target_dim != m_current_lod_max_dim) { + int requiredMemory = getMemRequiredForSize(target_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; } - return requiredMemory; - } else { - return 0; } } diff --git a/KREngine/KREngine/Classes/KRTexture.h b/KREngine/KREngine/Classes/KRTexture.h index 7b5365b..9acd3df 100644 --- a/KREngine/KREngine/Classes/KRTexture.h +++ b/KREngine/KREngine/Classes/KRTexture.h @@ -55,6 +55,8 @@ public: long getLastFrameUsed(); + virtual void resetPoolExpiry(); + protected: virtual bool createGLTexture(int lod_max_dim) = 0; GLuint getHandle(); diff --git a/KREngine/KREngine/Classes/KRTexture2D.cpp b/KREngine/KREngine/Classes/KRTexture2D.cpp index 1c0edda..849547e 100644 --- a/KREngine/KREngine/Classes/KRTexture2D.cpp +++ b/KREngine/KREngine/Classes/KRTexture2D.cpp @@ -75,7 +75,7 @@ bool KRTexture2D::createGLTexture(int lod_max_dim) { return true; } -void KRTexture2D::bind() { +void KRTexture2D::bind() { GLuint handle = getHandle(); GLDEBUG(glBindTexture(GL_TEXTURE_2D, handle)); diff --git a/KREngine/KREngine/Classes/KRTextureCube.cpp b/KREngine/KREngine/Classes/KRTextureCube.cpp index eedb838..1167a44 100644 --- a/KREngine/KREngine/Classes/KRTextureCube.cpp +++ b/KREngine/KREngine/Classes/KRTextureCube.cpp @@ -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(); diff --git a/KREngine/KREngine/Classes/KRTextureCube.h b/KREngine/KREngine/Classes/KRTextureCube.h index 9fb8af9..678f162 100644 --- a/KREngine/KREngine/Classes/KRTextureCube.h +++ b/KREngine/KREngine/Classes/KRTextureCube.h @@ -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); diff --git a/KREngine/KREngine/Classes/KRTextureManager.cpp b/KREngine/KREngine/Classes/KRTextureManager.cpp index a94f165..7e3ace2 100644 --- a/KREngine/KREngine/Classes/KRTextureManager.cpp +++ b/KREngine/KREngine/Classes/KRTextureManager.cpp @@ -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() diff --git a/KREngine/KREngine/Classes/KRTexturePVR.cpp b/KREngine/KREngine/Classes/KRTexturePVR.cpp index 4c0f919..9d45fd8 100644 --- a/KREngine/KREngine/Classes/KRTexturePVR.cpp +++ b/KREngine/KREngine/Classes/KRTexturePVR.cpp @@ -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::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; } @@ -177,8 +180,10 @@ long KRTexturePVR::getMemRequiredForSize(int max_dim) } bool KRTexturePVR::uploadTexture(GLenum target, int lod_max_dim, int ¤t_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; if(m_blocks.size() == 0) { @@ -195,7 +200,7 @@ bool KRTexturePVR::uploadTexture(GLenum target, int lod_max_dim, int ¤t_lo int i=0; for(std::list::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;