From 16616ae48d86da75b3809fa6be6c846a9d420603 Mon Sep 17 00:00:00 2001
From: Xiaohong Bao <bao@lindenlab.com>
Date: Thu, 23 May 2013 18:25:21 -0600
Subject: [PATCH] for SH-4145: Interesting: Implement occlusion culling for
 object cache

---
 indra/newview/llappviewer.cpp        |  31 +-
 indra/newview/llappviewer.h          |   3 +-
 indra/newview/lldrawable.cpp         |   2 +-
 indra/newview/lldrawpoolalpha.cpp    |  10 +-
 indra/newview/llscenemonitor.cpp     |   4 +-
 indra/newview/llspatialpartition.cpp | 678 ++-------------------------
 indra/newview/llspatialpartition.h   |  79 +---
 indra/newview/llvieweroctree.cpp     | 620 +++++++++++++++++++++++-
 indra/newview/llvieweroctree.h       |  99 +++-
 indra/newview/llviewerwindow.cpp     |   4 +-
 indra/newview/llvocache.cpp          |  41 +-
 indra/newview/llvovolume.cpp         |  12 +-
 indra/newview/pipeline.cpp           |  16 +-
 13 files changed, 845 insertions(+), 754 deletions(-)

diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 0b0db432c89..cb813ea8890 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1415,18 +1415,7 @@ bool LLAppViewer::mainLoop()
 					S32 io_pending = 0;
 					F32 max_time = llmin(gFrameIntervalSeconds.value() *10.f, 1.f);
 
-					{
-						LLFastTimer ftm(FTM_TEXTURE_CACHE);
- 						work_pending += LLAppViewer::getTextureCache()->update(max_time); // unpauses the texture cache thread
-					}
-					{
-						LLFastTimer ftm(FTM_DECODE);
-	 					work_pending += LLAppViewer::getImageDecodeThread()->update(max_time); // unpauses the image thread
-					}
-					{
-						LLFastTimer ftm(FTM_DECODE);
-	 					work_pending += LLAppViewer::getTextureFetch()->update(max_time); // unpauses the texture fetch thread
-					}
+					work_pending += updateTextureThreads(max_time);
 
 					{
 						LLFastTimer ftm(FTM_VFS);
@@ -1544,6 +1533,24 @@ bool LLAppViewer::mainLoop()
 	return true;
 }
 
+S32 LLAppViewer::updateTextureThreads(F32 max_time)
+{
+	S32 work_pending = 0;
+	{
+		LLFastTimer ftm(FTM_TEXTURE_CACHE);
+ 		work_pending += LLAppViewer::getTextureCache()->update(max_time); // unpauses the texture cache thread
+	}
+	{
+		LLFastTimer ftm(FTM_DECODE);
+	 	work_pending += LLAppViewer::getImageDecodeThread()->update(max_time); // unpauses the image thread
+	}
+	{
+		LLFastTimer ftm(FTM_DECODE);
+	 	work_pending += LLAppViewer::getTextureFetch()->update(max_time); // unpauses the texture fetch thread
+	}
+	return work_pending;
+}
+
 void LLAppViewer::flushVFSIO()
 {
 	while (1)
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index 627652dc308..30e208d01c6 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -171,7 +171,8 @@ class LLAppViewer : public LLApp
 
 	void purgeCache(); // Clear the local cache. 
 	void purgeCacheImmediate(); //clear local cache immediately.
-	
+	S32  updateTextureThreads(F32 max_time);
+
 	// mute/unmute the system's master audio
 	virtual void setMasterSystemAudioMute(bool mute);
 	virtual bool getMasterSystemAudioMute();	
diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp
index 4d72dd13435..17f610829d1 100644
--- a/indra/newview/lldrawable.cpp
+++ b/indra/newview/lldrawable.cpp
@@ -1145,7 +1145,7 @@ LLSpatialBridge::~LLSpatialBridge()
 	LLSpatialGroup* group = getSpatialGroup();
 	if (group)
 	{
-		group->mSpatialPartition->remove(this, group);
+		group->getSpatialPartition()->remove(this, group);
 	}
 	}
 
diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp
index 313b310e1e8..7020db917b8 100644
--- a/indra/newview/lldrawpoolalpha.cpp
+++ b/indra/newview/lldrawpoolalpha.cpp
@@ -351,7 +351,7 @@ void LLDrawPoolAlpha::renderAlphaHighlight(U32 mask)
 	for (LLCullResult::sg_iterator i = gPipeline.beginAlphaGroups(); i != gPipeline.endAlphaGroups(); ++i)
 	{
 		LLSpatialGroup* group = *i;
-		if (group->mSpatialPartition->mRenderByGroup &&
+		if (group->getSpatialPartition()->mRenderByGroup &&
 			!group->isDead())
 		{
 			LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[LLRenderPass::PASS_ALPHA];	
@@ -389,15 +389,15 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask)
 	{
 		LLSpatialGroup* group = *i;
 		llassert(group);
-		llassert(group->mSpatialPartition);
+		llassert(group->getSpatialPartition());
 
-		if (group->mSpatialPartition->mRenderByGroup &&
+		if (group->getSpatialPartition()->mRenderByGroup &&
 		    !group->isDead())
 		{
 			bool draw_glow_for_this_partition = mVertexShaderLevel > 0 && // no shaders = no glow.
 				// All particle systems seem to come off the wire with texture entries which claim that they glow.  This is probably a bug in the data.  Suppress.
-				group->mSpatialPartition->mPartitionType != LLViewerRegion::PARTITION_PARTICLE &&
-				group->mSpatialPartition->mPartitionType != LLViewerRegion::PARTITION_HUD_PARTICLE;
+				group->getSpatialPartition()->mPartitionType != LLViewerRegion::PARTITION_PARTICLE &&
+				group->getSpatialPartition()->mPartitionType != LLViewerRegion::PARTITION_HUD_PARTICLE;
 
 			LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[LLRenderPass::PASS_ALPHA];
 
diff --git a/indra/newview/llscenemonitor.cpp b/indra/newview/llscenemonitor.cpp
index 15fe77f028a..c592fd0a388 100644
--- a/indra/newview/llscenemonitor.cpp
+++ b/indra/newview/llscenemonitor.cpp
@@ -107,7 +107,7 @@ void LLSceneMonitor::reset()
 
 	if(mQueryObject > 0)
 	{
-		release_occlusion_query_object_name(mQueryObject);
+		LLOcclusionCullingGroup::releaseOcclusionQueryObjectName(mQueryObject);
 		mQueryObject = 0;
 	}
 }
@@ -437,7 +437,7 @@ void LLSceneMonitor::calcDiffAggregate()
 
 	if(!mQueryObject)
 	{
-		mQueryObject = get_new_occlusion_query_object_name();
+		mQueryObject = LLOcclusionCullingGroup::getNewOcclusionQueryObjectName();
 	}
 
 	LLGLDepthTest depth(true, false, GL_ALWAYS);
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index 53f5658815e..3f426d8f8a0 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -51,32 +51,16 @@
 #include "llvoavatar.h"
 #include "llvolumemgr.h"
 #include "lltextureatlas.h"
-#include "llglslshader.h"
 #include "llviewershadermgr.h"
 
 static LLFastTimer::DeclareTimer FTM_FRUSTUM_CULL("Frustum Culling");
 static LLFastTimer::DeclareTimer FTM_CULL_REBOUND("Cull Rebound Partition");
 
-const F32 SG_OCCLUSION_FUDGE = 0.25f;
-#define SG_DISCARD_TOLERANCE 0.01f
-
-#if LL_OCTREE_PARANOIA_CHECK
-#define assert_octree_valid(x) x->validate()
-#define assert_states_valid(x) ((LLSpatialGroup*) x->mSpatialPartition->mOctree->getListener(0))->checkStates()
-#else
-#define assert_octree_valid(x)
-#define assert_states_valid(x)
-#endif
-
 extern bool gShiftFrame;
 
 static U32 sZombieGroups = 0;
 U32 LLSpatialGroup::sNodeCount = 0;
 
-#define LL_TRACK_PENDING_OCCLUSION_QUERIES 0
-
-std::set<GLuint> LLSpatialGroup::sPendingQueries;
-
 U32 gOctreeMaxCapacity;
 
 BOOL LLSpatialGroup::sNoDelete = FALSE;
@@ -84,57 +68,6 @@ BOOL LLSpatialGroup::sNoDelete = FALSE;
 static F32 sLastMaxTexPriority = 1.f;
 static F32 sCurMaxTexPriority = 1.f;
 
-class LLOcclusionQueryPool : public LLGLNamePool
-{
-public:
-	LLOcclusionQueryPool()
-	{
-		mCurQuery = 1;
-	}
-
-protected:
-
-	std::list<GLuint> mAvailableName;
-	GLuint mCurQuery;
-		
-	virtual GLuint allocateName()
-	{
-		GLuint ret = 0;
-
-		if (!mAvailableName.empty())
-		{
-			ret = mAvailableName.front();
-			mAvailableName.pop_front();
-		}
-		else
-		{
-			ret = mCurQuery++;
-		}
-
-		return ret;
-	}
-
-	virtual void releaseName(GLuint name)
-	{
-#if LL_TRACK_PENDING_OCCLUSION_QUERIES
-		LLSpatialGroup::sPendingQueries.erase(name);
-#endif
-		llassert(std::find(mAvailableName.begin(), mAvailableName.end(), name) == mAvailableName.end());
-		mAvailableName.push_back(name);
-	}
-};
-
-static LLOcclusionQueryPool sQueryPool;
-
-GLuint get_new_occlusion_query_object_name()
-{
-	return sQueryPool.allocate();
-}
-
-void release_occlusion_query_object_name(GLuint name)
-{
-	sQueryPool.release(name);
-}
 
 //static counter for frame to switch LOD on
 
@@ -148,97 +81,6 @@ void sg_assert(BOOL expr)
 #endif
 }
 
-typedef enum
-{
-	b000 = 0x00,
-	b001 = 0x01,
-	b010 = 0x02,
-	b011 = 0x03,
-	b100 = 0x04,
-	b101 = 0x05,
-	b110 = 0x06,
-	b111 = 0x07,
-} eLoveTheBits;
-
-//contact Runitai Linden for a copy of the SL object used to write this table
-//basically, you give the table a bitmask of the look-at vector to a node and it
-//gives you a triangle fan index array
-static U16 sOcclusionIndices[] =
-{
-	 //000
-		b111, b110, b010, b011, b001, b101, b100, b110,
-	 //001 
-		b011, b010, b000, b001, b101, b111, b110, b010,
-	 //010
-		b101, b100, b110, b111, b011, b001, b000, b100,
-	 //011 
-		b001, b000, b100, b101, b111, b011, b010, b000,
-	 //100 
-		b110, b000, b010, b011, b111, b101, b100, b000,
-	 //101 
-		b010, b100, b000, b001, b011, b111, b110, b100,
-	 //110
-		b100, b010, b110, b111, b101, b001, b000, b010,
-	 //111
-		b000, b110, b100, b101, b001, b011, b010, b110,
-};
-
-U32 get_box_fan_indices(LLCamera* camera, const LLVector4a& center)
-{
-	LLVector4a origin;
-	origin.load3(camera->getOrigin().mV);
-
-	S32 cypher = center.greaterThan(origin).getGatheredBits() & 0x7;
-	
-	return cypher*8;
-}
-
-U8* get_box_fan_indices_ptr(LLCamera* camera, const LLVector4a& center)
-{
-	LLVector4a origin;
-	origin.load3(camera->getOrigin().mV);
-
-	S32 cypher = center.greaterThan(origin).getGatheredBits() & 0x7;
-	
-	return (U8*) (sOcclusionIndices+cypher*8);
-}
-
-//create a vertex buffer for efficiently rendering cubes
-LLVertexBuffer* ll_create_cube_vb(U32 type_mask, U32 usage)
-{
-	LLVertexBuffer* ret = new LLVertexBuffer(type_mask, usage);
-
-	ret->allocateBuffer(8, 64, true);
-
-	LLStrider<LLVector3> pos;
-	LLStrider<U16> idx;
-
-	ret->getVertexStrider(pos);
-	ret->getIndexStrider(idx);
-
-	pos[0] = LLVector3(-1,-1,-1);
-	pos[1] = LLVector3(-1,-1, 1);
-	pos[2] = LLVector3(-1, 1,-1);
-	pos[3] = LLVector3(-1, 1, 1);
-	pos[4] = LLVector3( 1,-1,-1);
-	pos[5] = LLVector3( 1,-1, 1);
-	pos[6] = LLVector3( 1, 1,-1);
-	pos[7] = LLVector3( 1, 1, 1);
-
-	for (U32 i = 0; i < 64; i++)
-	{
-		idx[i] = sOcclusionIndices[i];
-	}
-
-	ret->flush();
-
-	return ret;
-}
-
-static LLFastTimer::DeclareTimer FTM_BUILD_OCCLUSION("Build Occlusion");
-
-BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group);
-
 //returns:
 //	0 if sphere and AABB are not intersecting 
 //	1 if they are
@@ -285,18 +127,7 @@ LLSpatialGroup::~LLSpatialGroup()
 		sZombieGroups--;
 	}
 	
-	sNodeCount--;
-
-	if (gGLManager.mHasOcclusionQuery)
-	{
-		for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; ++i)
-		{
-			if (mOcclusionQuery[i])
-			{
-				release_occlusion_query_object_name(mOcclusionQuery[i]);
-			}
-		}
-	}
+	sNodeCount--;	
 
 	clearDrawMap();
 	clearAtlasList() ;
@@ -431,7 +262,7 @@ void LLSpatialGroup::clearDrawMap()
 
 BOOL LLSpatialGroup::isHUDGroup() 
 {
-	return mSpatialPartition && mSpatialPartition->isHUDPartition() ; 
+	return getSpatialPartition() && getSpatialPartition()->isHUDPartition() ; 
 }
 
 BOOL LLSpatialGroup::isRecentlyVisible() const
@@ -461,7 +292,7 @@ void LLSpatialGroup::validate()
 		sg_assert(drawable->getSpatialGroup() == this);
 		if (drawable->getSpatialBridge())
 		{
-			sg_assert(drawable->getSpatialBridge() == mSpatialPartition->asBridge());
+			sg_assert(drawable->getSpatialBridge() == getSpatialPartition()->asBridge());
 		}
 
 		/*if (drawable->isSpatialBridge())
@@ -501,14 +332,6 @@ void LLSpatialGroup::validate()
 #endif
 }
 
-void LLSpatialGroup::checkStates()
-{
-#if LL_OCTREE_PARANOIA_CHECK
-	//LLOctreeStateCheck checker;
-	//checker.traverse(mOctreeNode);
-#endif
-}
-
 void LLSpatialGroup::validateDrawMap()
 {
 #if LL_OCTREE_PARANOIA_CHECK
@@ -574,7 +397,7 @@ void LLSpatialGroup::rebuildGeom()
 {
 	if (!isDead())
 	{
-		mSpatialPartition->rebuildGeom(this);
+		getSpatialPartition()->rebuildGeom(this);
 
 		if (hasState(LLSpatialGroup::MESH_DIRTY))
 		{
@@ -587,7 +410,7 @@ void LLSpatialGroup::rebuildMesh()
 {
 	if (!isDead())
 	{
-		mSpatialPartition->rebuildMesh(this);
+		getSpatialPartition()->rebuildMesh(this);
 	}
 }
 
@@ -665,23 +488,7 @@ void LLSpatialPartition::rebuildMesh(LLSpatialGroup* group)
 
 LLSpatialGroup* LLSpatialGroup::getParent()
 {
-	if (isDead())
-	{
-		return NULL;
-	}
-
-	if(!mOctreeNode)
-	{
-		return NULL;
-	}
-	OctreeNode* parent = mOctreeNode->getOctParent();
-
-	if (parent)
-	{
-		return (LLSpatialGroup*) parent->getListener(0);
-	}
-
-	return NULL;
+	return (LLSpatialGroup*)LLviewerOctreeGroup::getParent();
 }
 
 BOOL LLSpatialGroup::removeObject(LLDrawable *drawablep, BOOL from_octree)
@@ -735,10 +542,10 @@ void LLSpatialGroup::shift(const LLVector4a &offset)
 	mObjectExtents[0].add(offset);
 	mObjectExtents[1].add(offset);
 
-	if (!mSpatialPartition->mRenderByGroup && 
-		mSpatialPartition->mPartitionType != LLViewerRegion::PARTITION_TREE &&
-		mSpatialPartition->mPartitionType != LLViewerRegion::PARTITION_TERRAIN &&
-		mSpatialPartition->mPartitionType != LLViewerRegion::PARTITION_BRIDGE)
+	if (!getSpatialPartition()->mRenderByGroup && 
+		getSpatialPartition()->mPartitionType != LLViewerRegion::PARTITION_TREE &&
+		getSpatialPartition()->mPartitionType != LLViewerRegion::PARTITION_TERRAIN &&
+		getSpatialPartition()->mPartitionType != LLViewerRegion::PARTITION_BRIDGE)
 	{
 		setState(GEOM_DIRTY);
 		gPipeline.markRebuild(this, TRUE);
@@ -839,135 +646,15 @@ void LLSpatialGroup::clearState(U32 state, S32 mode)
 	}
 }
 
-//=====================================
-//		Occlusion State Set/Clear
-//=====================================
-class LLSpatialSetOcclusionState : public OctreeTraveler
-{
-public:
-	U32 mState;
-	LLSpatialSetOcclusionState(U32 state) : mState(state) { }
-	virtual void visit(const OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->setOcclusionState(mState); }	
-};
-
-class LLSpatialSetOcclusionStateDiff : public LLSpatialSetOcclusionState
-{
-public:
-	LLSpatialSetOcclusionStateDiff(U32 state) : LLSpatialSetOcclusionState(state) { }
-
-	virtual void traverse(const OctreeNode* n)
-	{
-		LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
-		
-		if (!group->isOcclusionState(mState))
-		{
-			OctreeTraveler::traverse(n);
-		}
-	}
-};
-
-
-void LLSpatialGroup::setOcclusionState(U32 state, S32 mode) 
-{
-	if (mode > STATE_MODE_SINGLE)
-	{
-		if (mode == STATE_MODE_DIFF)
-		{
-			LLSpatialSetOcclusionStateDiff setter(state);
-			setter.traverse(mOctreeNode);
-		}
-		else if (mode == STATE_MODE_BRANCH)
-		{
-			LLSpatialSetOcclusionState setter(state);
-			setter.traverse(mOctreeNode);
-		}
-		else
-		{
-			for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
-			{
-				mOcclusionState[i] |= state;
-
-				if ((state & DISCARD_QUERY) && mOcclusionQuery[i])
-				{
-					release_occlusion_query_object_name(mOcclusionQuery[i]);
-					mOcclusionQuery[i] = 0;
-				}
-			}
-		}
-	}
-	else
-	{
-		mOcclusionState[LLViewerCamera::sCurCameraID] |= state;
-		if ((state & DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
-		{
-			release_occlusion_query_object_name(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
-			mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
-		}
-	}
-}
-
-class LLSpatialClearOcclusionState : public OctreeTraveler
-{
-public:
-	U32 mState;
-	
-	LLSpatialClearOcclusionState(U32 state) : mState(state) { }
-	virtual void visit(const OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->clearOcclusionState(mState); }
-};
-
-class LLSpatialClearOcclusionStateDiff : public LLSpatialClearOcclusionState
-{
-public:
-	LLSpatialClearOcclusionStateDiff(U32 state) : LLSpatialClearOcclusionState(state) { }
-
-	virtual void traverse(const OctreeNode* n)
-	{
-		LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
-		
-		if (group->isOcclusionState(mState))
-		{
-			OctreeTraveler::traverse(n);
-		}
-	}
-};
-
-void LLSpatialGroup::clearOcclusionState(U32 state, S32 mode)
-{
-	if (mode > STATE_MODE_SINGLE)
-	{
-		if (mode == STATE_MODE_DIFF)
-		{
-			LLSpatialClearOcclusionStateDiff clearer(state);
-			clearer.traverse(mOctreeNode);
-		}
-		else if (mode == STATE_MODE_BRANCH)
-		{
-			LLSpatialClearOcclusionState clearer(state);
-			clearer.traverse(mOctreeNode);
-		}
-		else
-		{
-			for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
-			{
-				mOcclusionState[i] &= ~state;
-			}
-		}
-	}
-	else
-	{
-		mOcclusionState[LLViewerCamera::sCurCameraID] &= ~state;
-	}
-}
 //======================================
 //		Octree Listener Implementation
 //======================================
 
-LLSpatialGroup::LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part) : LLviewerOctreeGroup(node),
+LLSpatialGroup::LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part) : LLOcclusionCullingGroup(node, part),
 	mObjectBoxSize(1.f),
 	mGeometryBytes(0),
 	mSurfaceArea(0.f),
-	mBuilt(0.f),
-	mSpatialPartition(part),
+	mBuilt(0.f),	
 	mVertexBuffer(NULL), 
 	mBufferUsage(part->mBufferUsage),
 	mDistance(0.f),
@@ -990,21 +677,6 @@ LLSpatialGroup::LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part) : LLv
 	setState(SG_INITIAL_STATE_MASK);
 	gPipeline.markRebuild(this, TRUE);
 
-	part->mLODSeed = (part->mLODSeed+1)%part->mLODPeriod;
-	mLODHash = part->mLODSeed;
-
-	OctreeNode* oct_parent = node->getOctParent();
-
-	LLSpatialGroup* parent = oct_parent ? (LLSpatialGroup*) oct_parent->getListener(0) : NULL;
-
-	for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
-	{
-		mOcclusionQuery[i] = 0;
-		mOcclusionIssued[i] = 0;
-		mOcclusionState[i] = parent ? SG_STATE_INHERIT_MASK & parent->mOcclusionState[i] : 0;
-		mVisible[i] = 0;
-	}
-
 	mRadius = 1;
 	mPixelArea = 1024.f;
 }
@@ -1030,10 +702,10 @@ void LLSpatialGroup::updateDistance(LLCamera &camera)
 #endif
 	if (!isEmpty())
 	{
-		mRadius = mSpatialPartition->mRenderByGroup ? mObjectBounds[1].getLength3().getF32() :
+		mRadius = getSpatialPartition()->mRenderByGroup ? mObjectBounds[1].getLength3().getF32() :
 						(F32) mOctreeNode->getSize().getLength3().getF32();
-		mDistance = mSpatialPartition->calcDistance(this, camera);
-		mPixelArea = mSpatialPartition->calcPixelArea(this, camera);
+		mDistance = getSpatialPartition()->calcDistance(this, camera);
+		mPixelArea = getSpatialPartition()->calcPixelArea(this, camera);
 	}
 }
 
@@ -1056,7 +728,7 @@ F32 LLSpatialPartition::calcDistance(LLSpatialGroup* group, LLCamera& camera)
 
 		if (!group->hasState(LLSpatialGroup::ALPHA_DIRTY))
 		{
-			if (!group->mSpatialPartition->isBridge())
+			if (!group->getSpatialPartition()->isBridge())
 			{
 				LLVector4a view_angle = eye;
 
@@ -1124,11 +796,6 @@ F32 LLSpatialGroup::getUpdateUrgency() const
 	}
 }
 
-BOOL LLSpatialGroup::needsUpdate()
-{
-	return (LLDrawable::getCurrentFrame()%mSpatialPartition->mLODPeriod == mLODHash) ? TRUE : FALSE;
-}
-
 BOOL LLSpatialGroup::changeLOD()
 {
 	if (hasState(ALPHA_DIRTY | OBJECT_DIRTY))
@@ -1136,11 +803,11 @@ BOOL LLSpatialGroup::changeLOD()
 		return TRUE;
 	}
 
-	if (mSpatialPartition->mSlopRatio > 0.f)
+	if (getSpatialPartition()->mSlopRatio > 0.f)
 	{
 		F32 ratio = (mDistance - mLastUpdateDistance)/(llmax(mLastUpdateDistance, mRadius));
 
-		if (fabsf(ratio) >= mSpatialPartition->mSlopRatio)
+		if (fabsf(ratio) >= getSpatialPartition()->mSlopRatio)
 		{
 			return TRUE;
 		}
@@ -1193,7 +860,7 @@ void LLSpatialGroup::handleDestruction(const TreeNode* node)
 	}
 	
 	//clean up avatar attachment stats
-	LLSpatialBridge* bridge = mSpatialPartition->asBridge();
+	LLSpatialBridge* bridge = getSpatialPartition()->asBridge();
 	if (bridge)
 	{
 		if (bridge->mAvatar.notNull())
@@ -1214,7 +881,7 @@ void LLSpatialGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* c
 {
 	if (child->getListenerCount() == 0)
 	{
-		new LLSpatialGroup(child, mSpatialPartition);
+		new LLSpatialGroup(child, getSpatialPartition());
 	}
 	else
 	{
@@ -1243,14 +910,7 @@ void LLSpatialGroup::destroyGL(bool keep_occlusion)
 
 	if (!keep_occlusion)
 	{
-		for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
-		{
-			if (mOcclusionQuery[i])
-			{
-				release_occlusion_query_object_name(mOcclusionQuery[i]);
-				mOcclusionQuery[i] = 0;
-			}
-		}
+		releaseOcclusionQueryObjectNames();
 	}
 
 
@@ -1272,233 +932,13 @@ void LLSpatialGroup::destroyGL(bool keep_occlusion)
 	}
 }
 
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_READBACK("Readback Occlusion");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_WAIT("Occlusion Wait");
-
-void LLSpatialGroup::checkOcclusion()
-{
-	if (LLPipeline::sUseOcclusion > 1)
-	{
-		LLFastTimer t(FTM_OCCLUSION_READBACK);
-		LLSpatialGroup* parent = getParent();
-		if (parent && parent->isOcclusionState(LLSpatialGroup::OCCLUDED))
-		{	//if the parent has been marked as occluded, the child is implicitly occluded
-			clearOcclusionState(QUERY_PENDING | DISCARD_QUERY);
-		}
-		else if (isOcclusionState(QUERY_PENDING))
-		{	//otherwise, if a query is pending, read it back
-
-			GLuint available = 0;
-			if (mOcclusionQuery[LLViewerCamera::sCurCameraID])
-			{
-				glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
-
-				static LLCachedControl<bool> wait_for_query(gSavedSettings, "RenderSynchronousOcclusion");
-
-				if (wait_for_query && mOcclusionIssued[LLViewerCamera::sCurCameraID] < gFrameCount)
-				{ //query was issued last frame, wait until it's available
-					S32 max_loop = 1024;
-					LLFastTimer t(FTM_OCCLUSION_WAIT);
-					while (!available && max_loop-- > 0)
-					{
-						F32 max_time = llmin(gFrameIntervalSeconds.value()*10.f, 1.f);
-						//do some usefu work while we wait
-						LLAppViewer::getTextureCache()->update(max_time); // unpauses the texture cache thread
-						LLAppViewer::getImageDecodeThread()->update(max_time); // unpauses the image thread
-						LLAppViewer::getTextureFetch()->update(max_time); // unpauses the texture fetch thread
-						
-						glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
-					}
-				}
-			}
-			else
-			{
-				available = 1;
-			}
-
-			if (available)
-			{ //result is available, read it back, otherwise wait until next frame
-				GLuint res = 1;
-				if (!isOcclusionState(DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
-				{
-					glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_ARB, &res);	
-#if LL_TRACK_PENDING_OCCLUSION_QUERIES
-					sPendingQueries.erase(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
-#endif
-				}
-				else if (mOcclusionQuery[LLViewerCamera::sCurCameraID])
-				{ //delete the query to avoid holding onto hundreds of pending queries
-					release_occlusion_query_object_name(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
-					mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
-				}
-				
-				if (isOcclusionState(DISCARD_QUERY))
-				{
-					res = 2;
-				}
-
-				if (res > 0)
-				{
-					assert_states_valid(this);
-					clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
-					assert_states_valid(this);
-				}
-				else
-				{
-					assert_states_valid(this);
-					
-					setOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
-					
-					assert_states_valid(this);
-				}
-
-				clearOcclusionState(QUERY_PENDING | DISCARD_QUERY);
-			}
-		}
-		else if (mSpatialPartition->isOcclusionEnabled() && isOcclusionState(LLSpatialGroup::OCCLUDED))
-		{	//check occlusion has been issued for occluded node that has not had a query issued
-			assert_states_valid(this);
-			clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
-			assert_states_valid(this);
-		}
-	}
-}
-
-static LLFastTimer::DeclareTimer FTM_PUSH_OCCLUSION_VERTS("Push Occlusion");
-static LLFastTimer::DeclareTimer FTM_SET_OCCLUSION_STATE("Occlusion State");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_EARLY_FAIL("Occlusion Early Fail");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_ALLOCATE("Allocate");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_BUILD("Build");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_BEGIN_QUERY("Begin Query");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_END_QUERY("End Query");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_SET_BUFFER("Set Buffer");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_DRAW_WATER("Draw Water");
-static LLFastTimer::DeclareTimer FTM_OCCLUSION_DRAW("Draw");
-
-
-
-void LLSpatialGroup::doOcclusion(LLCamera* camera)
-{
-	if (mSpatialPartition->isOcclusionEnabled() && LLPipeline::sUseOcclusion > 1)
-	{
-		// Don't cull hole/edge water, unless we have the GL_ARB_depth_clamp extension
-		if (earlyFail(camera, this))
-		{
-			LLFastTimer t(FTM_OCCLUSION_EARLY_FAIL);
-			setOcclusionState(LLSpatialGroup::DISCARD_QUERY);
-			assert_states_valid(this);
-			clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
-			assert_states_valid(this);
-		}
-		else
-		{
-			if (!isOcclusionState(QUERY_PENDING) || isOcclusionState(DISCARD_QUERY))
-			{
-				{ //no query pending, or previous query to be discarded
-					LLFastTimer t(FTM_RENDER_OCCLUSION);
-
-					if (!mOcclusionQuery[LLViewerCamera::sCurCameraID])
-					{
-						LLFastTimer t(FTM_OCCLUSION_ALLOCATE);
-						mOcclusionQuery[LLViewerCamera::sCurCameraID] = get_new_occlusion_query_object_name();
-					}
-
-					// Depth clamp all water to avoid it being culled as a result of being
-					// behind the far clip plane, and in the case of edge water to avoid
-					// it being culled while still visible.
-					bool const use_depth_clamp = gGLManager.mHasDepthClamp &&
-												(mSpatialPartition->mDrawableType == LLDrawPool::POOL_WATER ||						
-												mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER);
-
-					LLGLEnable clamp(use_depth_clamp ? GL_DEPTH_CLAMP : 0);				
-						
-#if !LL_DARWIN					
-					U32 mode = gGLManager.mHasOcclusionQuery2 ? GL_ANY_SAMPLES_PASSED : GL_SAMPLES_PASSED_ARB;
-#else
-					U32 mode = GL_SAMPLES_PASSED_ARB;
-#endif
-					
-#if LL_TRACK_PENDING_OCCLUSION_QUERIES
-					sPendingQueries.insert(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
-#endif
-
-					{
-						LLFastTimer t(FTM_PUSH_OCCLUSION_VERTS);
-						
-						//store which frame this query was issued on
-						mOcclusionIssued[LLViewerCamera::sCurCameraID] = gFrameCount;
-
-						{
-							LLFastTimer t(FTM_OCCLUSION_BEGIN_QUERY);
-							glBeginQueryARB(mode, mOcclusionQuery[LLViewerCamera::sCurCameraID]);					
-						}
-					
-						LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
-						llassert(shader);
-
-						shader->uniform3fv(LLShaderMgr::BOX_CENTER, 1, mBounds[0].getF32ptr());
-						shader->uniform3f(LLShaderMgr::BOX_SIZE, mBounds[1][0]+SG_OCCLUSION_FUDGE, 
-																 mBounds[1][1]+SG_OCCLUSION_FUDGE, 
-																 mBounds[1][2]+SG_OCCLUSION_FUDGE);
-
-						if (!use_depth_clamp && mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER)
-						{
-							LLFastTimer t(FTM_OCCLUSION_DRAW_WATER);
-
-							LLGLSquashToFarClip squash(glh_get_current_projection(), 1);
-							if (camera->getOrigin().isExactlyZero())
-							{ //origin is invalid, draw entire box
-								gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0);
-								gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8);				
-							}
-							else
-							{
-								gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, mBounds[0]));
-							}
-						}
-						else
-						{
-							LLFastTimer t(FTM_OCCLUSION_DRAW);
-							if (camera->getOrigin().isExactlyZero())
-							{ //origin is invalid, draw entire box
-								gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0);
-								gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8);				
-							}
-							else
-							{
-								gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, mBounds[0]));
-							}
-						}
-
-
-						{
-							LLFastTimer t(FTM_OCCLUSION_END_QUERY);
-							glEndQueryARB(mode);
-						}
-					}
-				}
-
-				{
-					LLFastTimer t(FTM_SET_OCCLUSION_STATE);
-					setOcclusionState(LLSpatialGroup::QUERY_PENDING);
-					clearOcclusionState(LLSpatialGroup::DISCARD_QUERY);
-				}
-			}
-		}
-	}
-}
-
 //==============================================
 
 LLSpatialPartition::LLSpatialPartition(U32 data_mask, BOOL render_by_group, U32 buffer_usage, LLViewerRegion* regionp)
 : mRenderByGroup(render_by_group), mBridge(NULL)
 {
-	mRegionp = regionp;
-	mOcclusionEnabled = TRUE;
-	mDrawableType = 0;
-	mPartitionType = LLViewerRegion::PARTITION_NONE;
-	mLODSeed = 0;
-	mLODPeriod = 1;
+	mRegionp = regionp;		
+	mPartitionType = LLViewerRegion::PARTITION_NONE;		
 	mVertexDataMask = data_mask;
 	mBufferUsage = buffer_usage;
 	mDepthMask = FALSE;
@@ -1566,11 +1006,11 @@ void LLSpatialPartition::move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL
 		
 	BOOL was_visible = curp ? curp->isVisible() : FALSE;
 
-	if (curp && curp->mSpatialPartition != this)
+	if (curp && curp->getSpatialPartition() != this)
 	{
 		//keep drawable from being garbage collected
 		LLPointer<LLDrawable> ptr = drawablep;
-		if (curp->mSpatialPartition->remove(drawablep, curp))
+		if (curp->getSpatialPartition()->remove(drawablep, curp))
 		{
 			put(drawablep, was_visible);
 			return;
@@ -1961,7 +1401,7 @@ class LLOctreeDirty : public OctreeTraveler
 			{
 				continue;
 			}
-			if (drawable->getVObj().notNull() && !group->mSpatialPartition->mRenderByGroup)
+			if (drawable->getVObj().notNull() && !group->getSpatialPartition()->mRenderByGroup)
 			{
 				gPipeline.markRebuild(drawable, LLDrawable::REBUILD_ALL, TRUE);
 			}
@@ -1985,11 +1425,6 @@ void LLSpatialPartition::resetVertexBuffers()
 	dirty.traverse(mOctree);
 }
 
-BOOL LLSpatialPartition::isOcclusionEnabled()
-{
-	return mOcclusionEnabled || LLPipeline::sUseOcclusion > 2;
-}
-
 BOOL LLSpatialPartition::getVisibleExtents(LLCamera& camera, LLVector3& visMin, LLVector3& visMax)
 {
 	LLVector4a visMina, visMaxa;
@@ -2075,51 +1510,6 @@ S32 LLSpatialPartition::cull(LLCamera &camera)
 	return 0;
 }
 
-BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group)
-{
-	if (camera->getOrigin().isExactlyZero())
-	{
-		return FALSE;
-	}
-
-	const F32 vel = SG_OCCLUSION_FUDGE*2.f;
-	LLVector4a fudge;
-	fudge.splat(vel);
-
-	const LLVector4a* bounds = group->getBounds();
-	const LLVector4a& c = bounds[0];
-	LLVector4a r;
-	r.setAdd(bounds[1], fudge);
-
-	/*if (r.magVecSquared() > 1024.0*1024.0)
-	{
-		return TRUE;
-	}*/
-
-	LLVector4a e;
-	e.load3(camera->getOrigin().mV);
-	
-	LLVector4a min;
-	min.setSub(c,r);
-	LLVector4a max;
-	max.setAdd(c,r);
-	
-	S32 lt = e.lessThan(min).getGatheredBits() & 0x7;
-	if (lt)
-	{
-		return FALSE;
-	}
-
-	S32 gt = e.greaterThan(max).getGatheredBits() & 0x7;
-	if (gt)
-	{
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-
 void pushVerts(LLDrawInfo* params, U32 mask)
 {
 	LLRenderPass::applyModelMatrix(*params);
@@ -2191,7 +1581,7 @@ void pushBufferVerts(LLVertexBuffer* buffer, U32 mask)
 
 void pushBufferVerts(LLSpatialGroup* group, U32 mask)
 {
-	if (group->mSpatialPartition->mRenderByGroup)
+	if (group->getSpatialPartition()->mRenderByGroup)
 	{
 		if (!group->mDrawMap.empty())
 		{
@@ -2292,7 +1682,7 @@ void renderOctree(LLSpatialGroup* group)
 				{
 					continue;
 				}
-				if (!group->mSpatialPartition->isBridge())
+				if (!group->getSpatialPartition()->isBridge())
 				{
 					gGL.pushMatrix();
 					LLVector3 trans = drawable->getRegion()->getOriginAgent();
@@ -2324,7 +1714,7 @@ void renderOctree(LLSpatialGroup* group)
 					}
 				}
 
-				if (!group->mSpatialPartition->isBridge())
+				if (!group->getSpatialPartition()->isBridge())
 				{
 					gGL.popMatrix();
 				}
@@ -2336,7 +1726,7 @@ void renderOctree(LLSpatialGroup* group)
 	else
 	{
 		if (group->mBufferUsage == GL_STATIC_DRAW_ARB && !group->isEmpty() 
-			&& group->mSpatialPartition->mRenderByGroup)
+			&& group->getSpatialPartition()->mRenderByGroup)
 		{
 			col.setVec(0.8f, 0.4f, 0.1f, 0.1f);
 		}
@@ -2373,7 +1763,7 @@ void renderOctree(LLSpatialGroup* group)
 		drawBoxOutline(bounds[0], bounds[1]);
 		
 		//draw bounding box for draw info
-		/*if (group->mSpatialPartition->mRenderByGroup)
+		/*if (group->getSpatialPartition()->mRenderByGroup)
 		{
 			gGL.diffuseColor4f(1.0f, 0.75f, 0.25f, 0.6f);
 			for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
@@ -3147,7 +2537,7 @@ void renderPhysicsShapes(LLSpatialGroup* group)
 		LLVOVolume* volume = drawable->getVOVolume();
 		if (volume && !volume->isAttachment() && volume->getPhysicsShapeType() != LLViewerObject::PHYSICS_SHAPE_NONE )
 		{
-			if (!group->mSpatialPartition->isBridge())
+			if (!group->getSpatialPartition()->isBridge())
 			{
 				gGL.pushMatrix();
 				LLVector3 trans = drawable->getRegion()->getOriginAgent();
@@ -4225,9 +3615,9 @@ class LLOctreeIntersect : public OctreeTraveler
 			LLVector3 local_start = mStart;
 			LLVector3 local_end   = mEnd;
 
-			if (group->mSpatialPartition->isBridge())
+			if (group->getSpatialPartition()->isBridge())
 			{
-				LLMatrix4 local_matrix = group->mSpatialPartition->asBridge()->mDrawable->getRenderMatrix();
+				LLMatrix4 local_matrix = group->getSpatialPartition()->asBridge()->mDrawable->getRenderMatrix();
 				local_matrix.invert();
 				
 				local_start = mStart * local_matrix;
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
index b592e73403b..406e796d4d6 100644
--- a/indra/newview/llspatialpartition.h
+++ b/indra/newview/llspatialpartition.h
@@ -55,12 +55,6 @@ class LLViewerRegion;
 
 void pushVerts(LLFace* face, U32 mask);
 
-// get index buffer for binary encoded axis vertex buffer given a box at center being viewed by given camera
-U32 get_box_fan_indices(LLCamera* camera, const LLVector4a& center);
-U8* get_box_fan_indices_ptr(LLCamera* camera, const LLVector4a& center);
-GLuint get_new_occlusion_query_object_name();
-void release_occlusion_query_object_name(GLuint name);
-
 class LLDrawInfo : public LLRefCount 
 {
 protected:
@@ -191,13 +185,13 @@ class LLDrawInfo : public LLRefCount
 };
 
 LL_ALIGN_PREFIX(16)
-class LLSpatialGroup : public LLviewerOctreeGroup
+class LLSpatialGroup : public LLOcclusionCullingGroup
 {
 	friend class LLSpatialPartition;
 	friend class LLOctreeStateCheck;
 public:
 
-	LLSpatialGroup(const LLSpatialGroup& rhs) : LLviewerOctreeGroup(rhs)
+	LLSpatialGroup(const LLSpatialGroup& rhs) : LLOcclusionCullingGroup(rhs)
 	{
 		*this = rhs;
 	}
@@ -218,7 +212,6 @@ class LLSpatialGroup : public LLviewerOctreeGroup
 		return *this;
 	}
 
-	static std::set<GLuint> sPendingQueries; //pending occlusion queries
 	static U32 sNodeCount;
 	static BOOL sNoDelete; //deletion of spatial groups and draw info not allowed if TRUE
 
@@ -256,17 +249,7 @@ class LLSpatialGroup : public LLviewerOctreeGroup
 
 	typedef enum
 	{
-		OCCLUDED				= 0x00010000,
-		QUERY_PENDING			= 0x00020000,
-		ACTIVE_OCCLUSION		= 0x00040000,
-		DISCARD_QUERY			= 0x00080000,
-		EARLY_FAIL				= 0x00100000,
-	} eOcclusionState;
-
-	typedef enum
-	{
-		DEAD					= LLviewerOctreeGroup::INVALID_STATE,
-		GEOM_DIRTY				= (DEAD << 1),
+		GEOM_DIRTY				= LLviewerOctreeGroup::INVALID_STATE,
 		ALPHA_DIRTY				= (GEOM_DIRTY << 1),
 		IN_IMAGE_QUEUE			= (ALPHA_DIRTY << 1),
 		IMAGE_DIRTY				= (IN_IMAGE_QUEUE << 1),
@@ -275,33 +258,19 @@ class LLSpatialGroup : public LLviewerOctreeGroup
 		IN_BUILD_Q1				= (NEW_DRAWINFO << 1),
 		IN_BUILD_Q2				= (IN_BUILD_Q1 << 1),
 		STATE_MASK				= 0x0000FFFF,
-	} eSpatialState;
-
-	typedef enum
-	{
-		STATE_MODE_SINGLE = 0,		//set one node
-		STATE_MODE_BRANCH,			//set entire branch
-		STATE_MODE_DIFF,			//set entire branch as long as current state is different
-		STATE_MODE_ALL_CAMERAS,		//used for occlusion state, set state for all cameras
-	} eSetStateMode;
+	} eSpatialState;	
 
 	LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part);
 
-	BOOL isHUDGroup() ;
-	BOOL isDead()							{ return hasState(DEAD); }
-	BOOL isOcclusionState(U32 state) const	{ return mOcclusionState[LLViewerCamera::sCurCameraID] & state ? TRUE : FALSE; }
+	BOOL isHUDGroup() ;	
 	
 	void clearDrawMap();
 	void validate();
-	void checkStates();
 	void validateDrawMap();
 	
 	void setState(U32 state, S32 mode);
 	void clearState(U32 state, S32 mode);
-	void clearState(U32 state)     {mState &= ~state;}	
-
-	void setOcclusionState(U32 state, S32 mode = STATE_MODE_SINGLE);
-	void clearOcclusionState(U32 state, S32 mode = STATE_MODE_SINGLE);
+	void clearState(U32 state)     {mState &= ~state;}		
 
 	LLSpatialGroup* getParent();
 
@@ -309,13 +278,10 @@ class LLSpatialGroup : public LLviewerOctreeGroup
 	BOOL removeObject(LLDrawable *drawablep, BOOL from_octree = FALSE);
 	BOOL updateInGroup(LLDrawable *drawablep, BOOL immediate = FALSE); // Update position if it's in the group
 	BOOL isRecentlyVisible() const;
-	void shift(const LLVector4a &offset);
-	void checkOcclusion(); //read back last occlusion query (if any)
-	void doOcclusion(LLCamera* camera); //issue occlusion query
+	void shift(const LLVector4a &offset);	
 	void destroyGL(bool keep_occlusion = false);
 	
 	void updateDistance(LLCamera& camera);
-	BOOL needsUpdate();
 	F32 getUpdateUrgency() const;
 	BOOL changeLOD();
 	void rebuildGeom();
@@ -327,6 +293,8 @@ class LLSpatialGroup : public LLviewerOctreeGroup
 
 	void drawObjectBox(LLColor4 col);
 
+	LLSpatialPartition* getSpatialPartition() {return (LLSpatialPartition*)mSpatialPartition;}
+
 	 //LISTENER FUNCTIONS
 	virtual void handleInsertion(const TreeNode* node, LLViewerOctreeEntry* face);
 	virtual void handleRemoval(const TreeNode* node, LLViewerOctreeEntry* face);
@@ -372,12 +340,8 @@ class LLSpatialGroup : public LLviewerOctreeGroup
 //-------------------
 
 protected:
-	virtual ~LLSpatialGroup();
-
-	U32 mOcclusionState[LLViewerCamera::NUM_CAMERAS];
-	U32 mOcclusionIssued[LLViewerCamera::NUM_CAMERAS];
-
-	S32 mLODHash;
+	virtual ~LLSpatialGroup();	
+	
 	static S32 sLODSeed;
 
 public:
@@ -387,11 +351,9 @@ class LLSpatialGroup : public LLviewerOctreeGroup
 	U32 mGeometryBytes; //used by volumes to track how many bytes of geometry data are in this node
 	F32 mSurfaceArea; //used by volumes to track estimated surface area of geometry in this node
 
-	F32 mBuilt;
-	LLSpatialPartition* mSpatialPartition;
+	F32 mBuilt;	
 	
-	LLPointer<LLVertexBuffer> mVertexBuffer;
-	GLuint					mOcclusionQuery[LLViewerCamera::NUM_CAMERAS];
+	LLPointer<LLVertexBuffer> mVertexBuffer;	
 
 	U32 mBufferUsage;
 	draw_map_t mDrawMap;
@@ -461,21 +423,18 @@ class LLSpatialPartition: public LLViewerOctreePartition, public LLGeometryManag
 	void renderDebug();
 	void renderIntersectingBBoxes(LLCamera* camera);
 	void restoreGL();
-	void resetVertexBuffers();
-	BOOL isOcclusionEnabled();
+	void resetVertexBuffers();	
+
 	BOOL getVisibleExtents(LLCamera& camera, LLVector3& visMin, LLVector3& visMax);
 
 public:
 	LLSpatialBridge* mBridge; // NULL for non-LLSpatialBridge instances, otherwise, mBridge == this
 							// use a pointer instead of making "isBridge" and "asBridge" virtual so it's safe
-							// to call asBridge() from the destructor
-	BOOL mOcclusionEnabled; // if TRUE, occlusion culling is performed
+							// to call asBridge() from the destructor	
+	
 	BOOL mInfiniteFarClip; // if TRUE, frustum culling ignores far clip plane
-	U32 mBufferUsage;
-	U32   mDrawableType;
-	const BOOL mRenderByGroup;
-	U32 mLODSeed;
-	U32 mLODPeriod;	//number of frames between LOD updates for a given spatial group (staggered by mLODSeed)
+	U32 mBufferUsage;	
+	const BOOL mRenderByGroup;	
 	U32 mVertexDataMask;
 	F32 mSlopRatio; //percentage distance must change before drawables receive LOD update (default is 0.25);
 	BOOL mDepthMask; //if TRUE, objects in this partition will be written to depth during alpha rendering
diff --git a/indra/newview/llvieweroctree.cpp b/indra/newview/llvieweroctree.cpp
index 926d791d1fb..844075089f5 100644
--- a/indra/newview/llvieweroctree.cpp
+++ b/indra/newview/llvieweroctree.cpp
@@ -27,6 +27,11 @@
 #include "llviewerprecompiledheaders.h"
 #include "llvieweroctree.h"
 #include "llviewerregion.h"
+#include "pipeline.h"
+#include "llviewercontrol.h"
+#include "llappviewer.h"
+#include "llglslshader.h"
+#include "llviewershadermgr.h"
 
 //-----------------------------------------------------------------------------------
 //static variables definitions
@@ -37,6 +42,100 @@ BOOL LLViewerOctreeDebug::sInDebug = FALSE;
 //-----------------------------------------------------------------------------------
 //some global functions definitions
 //-----------------------------------------------------------------------------------
+typedef enum
+{
+	b000 = 0x00,
+	b001 = 0x01,
+	b010 = 0x02,
+	b011 = 0x03,
+	b100 = 0x04,
+	b101 = 0x05,
+	b110 = 0x06,
+	b111 = 0x07,
+} eLoveTheBits;
+
+//contact Runitai Linden for a copy of the SL object used to write this table
+//basically, you give the table a bitmask of the look-at vector to a node and it
+//gives you a triangle fan index array
+static U16 sOcclusionIndices[] =
+{
+	 //000
+		b111, b110, b010, b011, b001, b101, b100, b110,
+	 //001 
+		b011, b010, b000, b001, b101, b111, b110, b010,
+	 //010
+		b101, b100, b110, b111, b011, b001, b000, b100,
+	 //011 
+		b001, b000, b100, b101, b111, b011, b010, b000,
+	 //100 
+		b110, b000, b010, b011, b111, b101, b100, b000,
+	 //101 
+		b010, b100, b000, b001, b011, b111, b110, b100,
+	 //110
+		b100, b010, b110, b111, b101, b001, b000, b010,
+	 //111
+		b000, b110, b100, b101, b001, b011, b010, b110,
+};
+
+U32 get_box_fan_indices(LLCamera* camera, const LLVector4a& center)
+{
+	LLVector4a origin;
+	origin.load3(camera->getOrigin().mV);
+
+	S32 cypher = center.greaterThan(origin).getGatheredBits() & 0x7;
+	
+	return cypher*8;
+}
+
+U8* get_box_fan_indices_ptr(LLCamera* camera, const LLVector4a& center)
+{
+	LLVector4a origin;
+	origin.load3(camera->getOrigin().mV);
+
+	S32 cypher = center.greaterThan(origin).getGatheredBits() & 0x7;
+	
+	return (U8*) (sOcclusionIndices+cypher*8);
+}
+
+//create a vertex buffer for efficiently rendering cubes
+LLVertexBuffer* ll_create_cube_vb(U32 type_mask, U32 usage)
+{
+	LLVertexBuffer* ret = new LLVertexBuffer(type_mask, usage);
+
+	ret->allocateBuffer(8, 64, true);
+
+	LLStrider<LLVector3> pos;
+	LLStrider<U16> idx;
+
+	ret->getVertexStrider(pos);
+	ret->getIndexStrider(idx);
+
+	pos[0] = LLVector3(-1,-1,-1);
+	pos[1] = LLVector3(-1,-1, 1);
+	pos[2] = LLVector3(-1, 1,-1);
+	pos[3] = LLVector3(-1, 1, 1);
+	pos[4] = LLVector3( 1,-1,-1);
+	pos[5] = LLVector3( 1,-1, 1);
+	pos[6] = LLVector3( 1, 1,-1);
+	pos[7] = LLVector3( 1, 1, 1);
+
+	for (U32 i = 0; i < 64; i++)
+	{
+		idx[i] = sOcclusionIndices[i];
+	}
+
+	ret->flush();
+
+	return ret;
+}
+
+
+#define LL_TRACK_PENDING_OCCLUSION_QUERIES 0
+
+const F32 SG_OCCLUSION_FUDGE = 0.25f;
+#define SG_DISCARD_TOLERANCE 0.01f
+
+
 S32 AABBSphereIntersect(const LLVector3& min, const LLVector3& max, const LLVector3 &origin, const F32 &rad)
 {
 	return AABBSphereIntersectR2(min, max, origin, rad*rad);
@@ -540,6 +639,11 @@ void LLviewerOctreeGroup::handleChildRemoval(const OctreeNode* parent, const Oct
 
 LLviewerOctreeGroup* LLviewerOctreeGroup::getParent()
 {
+	if (isDead())
+	{
+		return NULL;
+	}
+
 	if(!mOctreeNode)
 	{
 		return NULL;
@@ -629,10 +733,519 @@ void LLviewerOctreeGroup::setVisible()
 {
 	mVisible[LLViewerCamera::sCurCameraID] = LLViewerOctreeEntryData::getCurrentFrame();
 }
+
+void LLviewerOctreeGroup::checkStates()
+{
+#if LL_OCTREE_PARANOIA_CHECK
+	//LLOctreeStateCheck checker;
+	//checker.traverse(mOctreeNode);
+#endif
+}
+
+//-------------------------------------------------------------------------------------------
+//occulsion culling functions and classes
+//-------------------------------------------------------------------------------------------
+std::set<U32> LLOcclusionCullingGroup::sPendingQueries;
+class LLOcclusionQueryPool : public LLGLNamePool
+{
+public:
+	LLOcclusionQueryPool()
+	{
+		mCurQuery = 1;
+	}
+
+protected:
+
+	std::list<GLuint> mAvailableName;
+	GLuint mCurQuery;
+		
+	virtual GLuint allocateName()
+	{
+		GLuint ret = 0;
+
+		if (!mAvailableName.empty())
+		{
+			ret = mAvailableName.front();
+			mAvailableName.pop_front();
+		}
+		else
+		{
+			ret = mCurQuery++;
+		}
+
+		return ret;
+	}
+
+	virtual void releaseName(GLuint name)
+	{
+#if LL_TRACK_PENDING_OCCLUSION_QUERIES
+		LLSpatialGroup::sPendingQueries.erase(name);
+#endif
+		llassert(std::find(mAvailableName.begin(), mAvailableName.end(), name) == mAvailableName.end());
+		mAvailableName.push_back(name);
+	}
+};
+
+static LLOcclusionQueryPool sQueryPool;
+U32 LLOcclusionCullingGroup::getNewOcclusionQueryObjectName()
+{
+	return sQueryPool.allocate();
+}
+
+void LLOcclusionCullingGroup::releaseOcclusionQueryObjectName(GLuint name)
+{
+	sQueryPool.release(name);
+}
+
+//=====================================
+//		Occlusion State Set/Clear
+//=====================================
+class LLSpatialSetOcclusionState : public OctreeTraveler
+{
+public:
+	U32 mState;
+	LLSpatialSetOcclusionState(U32 state) : mState(state) { }
+	virtual void visit(const OctreeNode* branch) { ((LLOcclusionCullingGroup*) branch->getListener(0))->setOcclusionState(mState); }	
+};
+
+class LLSpatialSetOcclusionStateDiff : public LLSpatialSetOcclusionState
+{
+public:
+	LLSpatialSetOcclusionStateDiff(U32 state) : LLSpatialSetOcclusionState(state) { }
+
+	virtual void traverse(const OctreeNode* n)
+	{
+		LLOcclusionCullingGroup* group = (LLOcclusionCullingGroup*) n->getListener(0);
+		
+		if (!group->isOcclusionState(mState))
+		{
+			OctreeTraveler::traverse(n);
+		}
+	}
+};
+
+
+LLOcclusionCullingGroup::LLOcclusionCullingGroup(OctreeNode* node, LLViewerOctreePartition* part) : 
+	LLviewerOctreeGroup(node),
+	mSpatialPartition(part)
+{
+	part->mLODSeed = (part->mLODSeed+1)%part->mLODPeriod;
+	mLODHash = part->mLODSeed;
+
+	OctreeNode* oct_parent = node->getOctParent();
+	LLOcclusionCullingGroup* parent = oct_parent ? (LLOcclusionCullingGroup*) oct_parent->getListener(0) : NULL;
+
+	for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
+	{
+		mOcclusionQuery[i] = 0;
+		mOcclusionIssued[i] = 0;
+		mOcclusionState[i] = parent ? SG_STATE_INHERIT_MASK & parent->mOcclusionState[i] : 0;
+		mVisible[i] = 0;
+	}
+}
+
+LLOcclusionCullingGroup::~LLOcclusionCullingGroup()
+{
+	releaseOcclusionQueryObjectNames();
+}
+
+BOOL LLOcclusionCullingGroup::needsUpdate()
+{
+	return (LLDrawable::getCurrentFrame() % mSpatialPartition->mLODPeriod == mLODHash) ? TRUE : FALSE;
+}
+
+//virtual 
+void LLOcclusionCullingGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* child)
+{
+	if (child->getListenerCount() == 0)
+	{
+		new LLOcclusionCullingGroup(child, mSpatialPartition);
+	}
+	else
+	{
+		OCT_ERRS << "LLOcclusionCullingGroup redundancy detected." << llendl;
+	}
+
+	unbound();
+	
+	((LLviewerOctreeGroup*)child->getListener(0))->unbound();
+}
+
+void LLOcclusionCullingGroup::releaseOcclusionQueryObjectNames()
+{
+	if (gGLManager.mHasOcclusionQuery)
+	{
+		for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; ++i)
+		{
+			if (mOcclusionQuery[i])
+			{
+				releaseOcclusionQueryObjectName(mOcclusionQuery[i]);
+				mOcclusionQuery[i] = 0;
+			}
+		}
+	}
+}
+
+void LLOcclusionCullingGroup::setOcclusionState(U32 state, S32 mode) 
+{
+	if (mode > STATE_MODE_SINGLE)
+	{
+		if (mode == STATE_MODE_DIFF)
+		{
+			LLSpatialSetOcclusionStateDiff setter(state);
+			setter.traverse(mOctreeNode);
+		}
+		else if (mode == STATE_MODE_BRANCH)
+		{
+			LLSpatialSetOcclusionState setter(state);
+			setter.traverse(mOctreeNode);
+		}
+		else
+		{
+			for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
+			{
+				mOcclusionState[i] |= state;
+
+				if ((state & DISCARD_QUERY) && mOcclusionQuery[i])
+				{
+					releaseOcclusionQueryObjectName(mOcclusionQuery[i]);
+					mOcclusionQuery[i] = 0;
+				}
+			}
+		}
+	}
+	else
+	{
+		mOcclusionState[LLViewerCamera::sCurCameraID] |= state;
+		if ((state & DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
+		{
+			releaseOcclusionQueryObjectName(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
+			mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
+		}
+	}
+}
+
+class LLSpatialClearOcclusionState : public OctreeTraveler
+{
+public:
+	U32 mState;
+	
+	LLSpatialClearOcclusionState(U32 state) : mState(state) { }
+	virtual void visit(const OctreeNode* branch) { ((LLOcclusionCullingGroup*) branch->getListener(0))->clearOcclusionState(mState); }
+};
+
+class LLSpatialClearOcclusionStateDiff : public LLSpatialClearOcclusionState
+{
+public:
+	LLSpatialClearOcclusionStateDiff(U32 state) : LLSpatialClearOcclusionState(state) { }
+
+	virtual void traverse(const OctreeNode* n)
+	{
+		LLOcclusionCullingGroup* group = (LLOcclusionCullingGroup*) n->getListener(0);
+		
+		if (group->isOcclusionState(mState))
+		{
+			OctreeTraveler::traverse(n);
+		}
+	}
+};
+
+void LLOcclusionCullingGroup::clearOcclusionState(U32 state, S32 mode)
+{
+	if (mode > STATE_MODE_SINGLE)
+	{
+		if (mode == STATE_MODE_DIFF)
+		{
+			LLSpatialClearOcclusionStateDiff clearer(state);
+			clearer.traverse(mOctreeNode);
+		}
+		else if (mode == STATE_MODE_BRANCH)
+		{
+			LLSpatialClearOcclusionState clearer(state);
+			clearer.traverse(mOctreeNode);
+		}
+		else
+		{
+			for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
+			{
+				mOcclusionState[i] &= ~state;
+			}
+		}
+	}
+	else
+	{
+		mOcclusionState[LLViewerCamera::sCurCameraID] &= ~state;
+	}
+}
+
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_READBACK("Readback Occlusion");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_WAIT("Occlusion Wait");
+
+BOOL LLOcclusionCullingGroup::earlyFail(LLCamera* camera)
+{
+	if (camera->getOrigin().isExactlyZero())
+	{
+		return FALSE;
+	}
+
+	const F32 vel = SG_OCCLUSION_FUDGE*2.f;
+	LLVector4a fudge;
+	fudge.splat(vel);
+
+	const LLVector4a* bounds = getBounds();
+	const LLVector4a& c = bounds[0];
+	LLVector4a r;
+	r.setAdd(bounds[1], fudge);
+
+	/*if (r.magVecSquared() > 1024.0*1024.0)
+	{
+		return TRUE;
+	}*/
+
+	LLVector4a e;
+	e.load3(camera->getOrigin().mV);
+	
+	LLVector4a min;
+	min.setSub(c,r);
+	LLVector4a max;
+	max.setAdd(c,r);
+	
+	S32 lt = e.lessThan(min).getGatheredBits() & 0x7;
+	if (lt)
+	{
+		return FALSE;
+	}
+
+	S32 gt = e.greaterThan(max).getGatheredBits() & 0x7;
+	if (gt)
+	{
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void LLOcclusionCullingGroup::checkOcclusion()
+{
+	if (LLPipeline::sUseOcclusion > 1)
+	{
+		LLFastTimer t(FTM_OCCLUSION_READBACK);
+		LLOcclusionCullingGroup* parent = (LLOcclusionCullingGroup*)getParent();
+		if (parent && parent->isOcclusionState(LLOcclusionCullingGroup::OCCLUDED))
+		{	//if the parent has been marked as occluded, the child is implicitly occluded
+			clearOcclusionState(QUERY_PENDING | DISCARD_QUERY);
+		}
+		else if (isOcclusionState(QUERY_PENDING))
+		{	//otherwise, if a query is pending, read it back
+
+			GLuint available = 0;
+			if (mOcclusionQuery[LLViewerCamera::sCurCameraID])
+			{
+				glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
+
+				static LLCachedControl<bool> wait_for_query(gSavedSettings, "RenderSynchronousOcclusion");
+
+				if (wait_for_query && mOcclusionIssued[LLViewerCamera::sCurCameraID] < gFrameCount)
+				{ //query was issued last frame, wait until it's available
+					S32 max_loop = 1024;
+					LLFastTimer t(FTM_OCCLUSION_WAIT);
+					while (!available && max_loop-- > 0)
+					{
+						//do some usefu work while we wait
+						F32 max_time = llmin(gFrameIntervalSeconds.value()*10.f, 1.f);
+						LLAppViewer::instance()->updateTextureThreads(max_time);
+						
+						glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
+					}
+				}
+			}
+			else
+			{
+				available = 1;
+			}
+
+			if (available)
+			{ //result is available, read it back, otherwise wait until next frame
+				GLuint res = 1;
+				if (!isOcclusionState(DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
+				{
+					glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_ARB, &res);	
+#if LL_TRACK_PENDING_OCCLUSION_QUERIES
+					sPendingQueries.erase(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
+#endif
+				}
+				else if (mOcclusionQuery[LLViewerCamera::sCurCameraID])
+				{ //delete the query to avoid holding onto hundreds of pending queries
+					releaseOcclusionQueryObjectName(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
+					mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
+				}
+				
+				if (isOcclusionState(DISCARD_QUERY))
+				{
+					res = 2;
+				}
+
+				if (res > 0)
+				{
+					assert_states_valid(this);
+					clearOcclusionState(LLOcclusionCullingGroup::OCCLUDED, LLOcclusionCullingGroup::STATE_MODE_DIFF);
+					assert_states_valid(this);
+				}
+				else
+				{
+					assert_states_valid(this);
+					
+					setOcclusionState(LLOcclusionCullingGroup::OCCLUDED, LLOcclusionCullingGroup::STATE_MODE_DIFF);
+					
+					assert_states_valid(this);
+				}
+
+				clearOcclusionState(QUERY_PENDING | DISCARD_QUERY);
+			}
+		}
+		else if (mSpatialPartition->isOcclusionEnabled() && isOcclusionState(LLOcclusionCullingGroup::OCCLUDED))
+		{	//check occlusion has been issued for occluded node that has not had a query issued
+			assert_states_valid(this);
+			clearOcclusionState(LLOcclusionCullingGroup::OCCLUDED, LLOcclusionCullingGroup::STATE_MODE_DIFF);
+			assert_states_valid(this);
+		}
+	}
+}
+
+static LLFastTimer::DeclareTimer FTM_PUSH_OCCLUSION_VERTS("Push Occlusion");
+static LLFastTimer::DeclareTimer FTM_SET_OCCLUSION_STATE("Occlusion State");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_EARLY_FAIL("Occlusion Early Fail");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_ALLOCATE("Allocate");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_BUILD("Build");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_BEGIN_QUERY("Begin Query");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_END_QUERY("End Query");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_SET_BUFFER("Set Buffer");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_DRAW_WATER("Draw Water");
+static LLFastTimer::DeclareTimer FTM_OCCLUSION_DRAW("Draw");
+
+void LLOcclusionCullingGroup::doOcclusion(LLCamera* camera)
+{
+	if (mSpatialPartition->isOcclusionEnabled() && LLPipeline::sUseOcclusion > 1)
+	{
+		// Don't cull hole/edge water, unless we have the GL_ARB_depth_clamp extension
+		if (earlyFail(camera))
+		{
+			LLFastTimer t(FTM_OCCLUSION_EARLY_FAIL);
+			setOcclusionState(LLOcclusionCullingGroup::DISCARD_QUERY);
+			assert_states_valid(this);
+			clearOcclusionState(LLOcclusionCullingGroup::OCCLUDED, LLOcclusionCullingGroup::STATE_MODE_DIFF);
+			assert_states_valid(this);
+		}
+		else
+		{
+			if (!isOcclusionState(QUERY_PENDING) || isOcclusionState(DISCARD_QUERY))
+			{
+				{ //no query pending, or previous query to be discarded
+					LLFastTimer t(FTM_RENDER_OCCLUSION);
+
+					if (!mOcclusionQuery[LLViewerCamera::sCurCameraID])
+					{
+						LLFastTimer t(FTM_OCCLUSION_ALLOCATE);
+						mOcclusionQuery[LLViewerCamera::sCurCameraID] = getNewOcclusionQueryObjectName();
+					}
+
+					// Depth clamp all water to avoid it being culled as a result of being
+					// behind the far clip plane, and in the case of edge water to avoid
+					// it being culled while still visible.
+					bool const use_depth_clamp = gGLManager.mHasDepthClamp &&
+												(mSpatialPartition->mDrawableType == LLDrawPool::POOL_WATER ||						
+												mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER);
+
+					LLGLEnable clamp(use_depth_clamp ? GL_DEPTH_CLAMP : 0);				
+						
+#if !LL_DARWIN					
+					U32 mode = gGLManager.mHasOcclusionQuery2 ? GL_ANY_SAMPLES_PASSED : GL_SAMPLES_PASSED_ARB;
+#else
+					U32 mode = GL_SAMPLES_PASSED_ARB;
+#endif
+					
+#if LL_TRACK_PENDING_OCCLUSION_QUERIES
+					sPendingQueries.insert(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
+#endif
+
+					{
+						LLFastTimer t(FTM_PUSH_OCCLUSION_VERTS);
+						
+						//store which frame this query was issued on
+						mOcclusionIssued[LLViewerCamera::sCurCameraID] = gFrameCount;
+
+						{
+							LLFastTimer t(FTM_OCCLUSION_BEGIN_QUERY);
+							glBeginQueryARB(mode, mOcclusionQuery[LLViewerCamera::sCurCameraID]);					
+						}
+					
+						LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+						llassert(shader);
+
+						shader->uniform3fv(LLShaderMgr::BOX_CENTER, 1, mBounds[0].getF32ptr());
+						shader->uniform3f(LLShaderMgr::BOX_SIZE, mBounds[1][0]+SG_OCCLUSION_FUDGE, 
+																 mBounds[1][1]+SG_OCCLUSION_FUDGE, 
+																 mBounds[1][2]+SG_OCCLUSION_FUDGE);
+
+						if (!use_depth_clamp && mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER)
+						{
+							LLFastTimer t(FTM_OCCLUSION_DRAW_WATER);
+
+							LLGLSquashToFarClip squash(glh_get_current_projection(), 1);
+							if (camera->getOrigin().isExactlyZero())
+							{ //origin is invalid, draw entire box
+								gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0);
+								gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8);				
+							}
+							else
+							{
+								gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, mBounds[0]));
+							}
+						}
+						else
+						{
+							LLFastTimer t(FTM_OCCLUSION_DRAW);
+							if (camera->getOrigin().isExactlyZero())
+							{ //origin is invalid, draw entire box
+								gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0);
+								gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8);				
+							}
+							else
+							{
+								gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, mBounds[0]));
+							}
+						}
+
+
+						{
+							LLFastTimer t(FTM_OCCLUSION_END_QUERY);
+							glEndQueryARB(mode);
+						}
+					}
+				}
+
+				{
+					LLFastTimer t(FTM_SET_OCCLUSION_STATE);
+					setOcclusionState(LLOcclusionCullingGroup::QUERY_PENDING);
+					clearOcclusionState(LLOcclusionCullingGroup::DISCARD_QUERY);
+				}
+			}
+		}
+	}
+}
+//-------------------------------------------------------------------------------------------
+//end of occulsion culling functions and classes
+//-------------------------------------------------------------------------------------------
+
 //-----------------------------------------------------------------------------------
 //class LLViewerOctreePartition definitions
 //-----------------------------------------------------------------------------------
-LLViewerOctreePartition::LLViewerOctreePartition() : mRegionp(NULL)
+LLViewerOctreePartition::LLViewerOctreePartition() : 
+	mRegionp(NULL), 
+	mOcclusionEnabled(TRUE), 
+	mDrawableType(0),
+	mLODSeed(0),
+	mLODPeriod(1)
 {
 	LLVector4a center, size;
 	center.splat(0.f);
@@ -647,6 +1260,11 @@ LLViewerOctreePartition::~LLViewerOctreePartition()
 	mOctree = NULL;
 }
 
+BOOL LLViewerOctreePartition::isOcclusionEnabled()
+{
+	return mOcclusionEnabled || LLPipeline::sUseOcclusion > 2;
+}
+
 //-----------------------------------------------------------------------------------
 //class LLViewerOctreeCull definitions
 //-----------------------------------------------------------------------------------
diff --git a/indra/newview/llvieweroctree.h b/indra/newview/llvieweroctree.h
index b6faf4c7ba7..ed77e4bb7e4 100644
--- a/indra/newview/llvieweroctree.h
+++ b/indra/newview/llvieweroctree.h
@@ -43,6 +43,7 @@ class LLViewerRegion;
 class LLViewerOctreeEntryData;
 class LLviewerOctreeGroup;
 class LLViewerOctreeEntry;
+class LLViewerOctreePartition;
 
 typedef LLOctreeListener<LLViewerOctreeEntry>	OctreeListener;
 typedef LLTreeNode<LLViewerOctreeEntry>			TreeNode;
@@ -50,6 +51,18 @@ typedef LLOctreeNode<LLViewerOctreeEntry>		OctreeNode;
 typedef LLOctreeRoot<LLViewerOctreeEntry>		OctreeRoot;
 typedef LLOctreeTraveler<LLViewerOctreeEntry>	OctreeTraveler;
 
+#if LL_OCTREE_PARANOIA_CHECK
+#define assert_octree_valid(x) x->validate()
+#define assert_states_valid(x) ((LLviewerOctreeGroup*) x->mSpatialPartition->mOctree->getListener(0))->checkStates()
+#else
+#define assert_octree_valid(x)
+#define assert_states_valid(x)
+#endif
+
+// get index buffer for binary encoded axis vertex buffer given a box at center being viewed by given camera
+U32 get_box_fan_indices(LLCamera* camera, const LLVector4a& center);
+U8* get_box_fan_indices_ptr(LLCamera* camera, const LLVector4a& center);
+
 S32 AABBSphereIntersect(const LLVector4a& min, const LLVector4a& max, const LLVector3 &origin, const F32 &rad);
 S32 AABBSphereIntersectR2(const LLVector4a& min, const LLVector4a& max, const LLVector3 &origin, const F32 &radius_squared);
 
@@ -181,14 +194,15 @@ class LLviewerOctreeGroup : public LLOctreeListener<LLViewerOctreeEntry>
 protected:
 	~LLviewerOctreeGroup();
 
-public:
+public:	
 	enum
 	{
 		CLEAN              = 0x00000000,
 		DIRTY              = 0x00000001,
 		OBJECT_DIRTY       = 0x00000002,
 		SKIP_FRUSTUM_CHECK = 0x00000004,
-		INVALID_STATE      = 0x00000008,
+		DEAD               = 0x00000008,
+		INVALID_STATE      = 0x00000010,
 	};
 
 public:
@@ -216,6 +230,8 @@ class LLviewerOctreeGroup : public LLOctreeListener<LLViewerOctreeEntry>
 
 	virtual void unbound();
 	virtual void rebound();
+	
+	BOOL isDead()							{ return hasState(DEAD); }	
 
 	void setVisible();
 	BOOL isVisible() const;
@@ -251,9 +267,11 @@ class LLviewerOctreeGroup : public LLOctreeListener<LLViewerOctreeEntry>
 	U32 getElementCount() const { return mOctreeNode->getElementCount(); }
 	bool hasElement(LLViewerOctreeEntryData* data);
 	
+protected:
+	void checkStates();
 private:
-	virtual bool boundObjects(BOOL empty, LLVector4a& minOut, LLVector4a& maxOut);	
-	
+	virtual bool boundObjects(BOOL empty, LLVector4a& minOut, LLVector4a& maxOut);			
+
 protected:
 	U32         mState;
 	OctreeNode* mOctreeNode;	
@@ -261,12 +279,76 @@ class LLviewerOctreeGroup : public LLOctreeListener<LLViewerOctreeEntry>
 	LL_ALIGN_16(LLVector4a mBounds[2]);        // bounding box (center, size) of this node and all its children (tight fit to objects)
 	LL_ALIGN_16(LLVector4a mObjectBounds[2]);  // bounding box (center, size) of objects in this node
 	LL_ALIGN_16(LLVector4a mExtents[2]);       // extents (min, max) of this node and all its children
-	LL_ALIGN_16(LLVector4a mObjectExtents[2]); // extents (min, max) of objects in this node
+	LL_ALIGN_16(LLVector4a mObjectExtents[2]); // extents (min, max) of objects in this node	
 
 public:
-	S32         mVisible[LLViewerCamera::NUM_CAMERAS];
+	S32         mVisible[LLViewerCamera::NUM_CAMERAS];	
+
 }LL_ALIGN_POSTFIX(16);
 
+//octree group which has capability to support occlusion culling
+//LL_ALIGN_PREFIX(16)
+class LLOcclusionCullingGroup : public LLviewerOctreeGroup
+{
+public:
+	typedef enum
+	{
+		OCCLUDED				= 0x00010000,
+		QUERY_PENDING			= 0x00020000,
+		ACTIVE_OCCLUSION		= 0x00040000,
+		DISCARD_QUERY			= 0x00080000,
+		EARLY_FAIL				= 0x00100000,
+	} eOcclusionState;
+
+	typedef enum
+	{
+		STATE_MODE_SINGLE = 0,		//set one node
+		STATE_MODE_BRANCH,			//set entire branch
+		STATE_MODE_DIFF,			//set entire branch as long as current state is different
+		STATE_MODE_ALL_CAMERAS,		//used for occlusion state, set state for all cameras
+	} eSetStateMode;
+
+public:
+	LLOcclusionCullingGroup(OctreeNode* node, LLViewerOctreePartition* part);
+	LLOcclusionCullingGroup(const LLOcclusionCullingGroup& rhs) : LLviewerOctreeGroup(rhs)
+	{
+		*this = rhs;
+	}
+	~LLOcclusionCullingGroup();
+
+	void setOcclusionState(U32 state, S32 mode = STATE_MODE_SINGLE);
+	void clearOcclusionState(U32 state, S32 mode = STATE_MODE_SINGLE);
+	void checkOcclusion(); //read back last occlusion query (if any)
+	void doOcclusion(LLCamera* camera); //issue occlusion query
+	BOOL isOcclusionState(U32 state) const	{ return mOcclusionState[LLViewerCamera::sCurCameraID] & state ? TRUE : FALSE; }		
+	
+	BOOL needsUpdate();
+
+	//virtual 
+	void handleChildAddition(const OctreeNode* parent, OctreeNode* child);
+
+	static U32 getNewOcclusionQueryObjectName();
+	static void releaseOcclusionQueryObjectName(U32 name);
+
+protected:
+	void releaseOcclusionQueryObjectNames();
+
+private:	
+	BOOL earlyFail(LLCamera* camera);
+
+protected:
+	U32         mOcclusionState[LLViewerCamera::NUM_CAMERAS];
+	U32         mOcclusionIssued[LLViewerCamera::NUM_CAMERAS];
+
+	S32         mLODHash;
+
+	LLViewerOctreePartition* mSpatialPartition;
+	U32		                 mOcclusionQuery[LLViewerCamera::NUM_CAMERAS];
+
+public:		
+	static std::set<U32> sPendingQueries;
+};//LL_ALIGN_POSTFIX(16);
+
 class LLViewerOctreePartition
 {
 public:
@@ -275,11 +357,16 @@ class LLViewerOctreePartition
 
 	// Cull on arbitrary frustum
 	virtual S32 cull(LLCamera &camera) = 0;
+	BOOL isOcclusionEnabled();
 
 public:	
 	U32              mPartitionType;
+	U32              mDrawableType;
 	OctreeNode*      mOctree;
 	LLViewerRegion*  mRegionp; // the region this partition belongs to.
+	BOOL             mOcclusionEnabled; // if TRUE, occlusion culling is performed
+	U32              mLODSeed;
+	U32              mLODPeriod;	//number of frames between LOD updates for a given spatial group (staggered by mLODSeed)
 };
 
 class LLViewerOctreeCull : public OctreeTraveler
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index fea44a38c6a..eb004106c36 100755
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -594,9 +594,9 @@ class LLDebugText
 			
 			ypos += y_inc;
 
-			if (!LLSpatialGroup::sPendingQueries.empty())
+			if (!LLOcclusionCullingGroup::sPendingQueries.empty())
 			{
-				addText(xpos,ypos, llformat("%d Queries pending", LLSpatialGroup::sPendingQueries.size()));
+				addText(xpos,ypos, llformat("%d Queries pending", LLOcclusionCullingGroup::sPendingQueries.size()));
 				ypos += y_inc;
 			}
 
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
index eba768fef4f..62615407650 100644
--- a/indra/newview/llvocache.cpp
+++ b/indra/newview/llvocache.cpp
@@ -372,10 +372,11 @@ void LLVOCacheEntry::setBoundingInfo(const LLVector3& pos, const LLVector3& scal
 //-------------------------------------------------------------------
 LLVOCachePartition::LLVOCachePartition(LLViewerRegion* regionp)
 {
+	mLODPeriod = 16;
 	mRegionp = regionp;
 	mPartitionType = LLViewerRegion::PARTITION_VO_CACHE;
 	
-	new LLviewerOctreeGroup(mOctree);
+	new LLOcclusionCullingGroup(mOctree, this);
 }
 
 void LLVOCachePartition::addEntry(LLViewerOctreeEntry* entry)
@@ -400,11 +401,31 @@ class LLVOCacheOctreeCull : public LLViewerOctreeCull
 		mLocalShift = shift;
 	}
 
+	virtual bool earlyFail(LLviewerOctreeGroup* base_group)
+	{
+		LLOcclusionCullingGroup* group = (LLOcclusionCullingGroup*)base_group;
+		if(group->needsUpdate())
+		{
+			return false; //needs to issue new occlusion culling check.
+		}
+
+		group->checkOcclusion();
+
+		if (group->getOctreeNode()->getParent() &&	//never occlusion cull the root node
+		  	LLPipeline::sUseOcclusion &&			//ignore occlusion if disabled			
+			group->isOcclusionState(LLSpatialGroup::OCCLUDED))
+		{
+			return true;
+		}
+		
+		return false;
+	}
+
 	virtual S32 frustumCheck(const LLviewerOctreeGroup* group)
 	{
-		//S32 res = AABBInRegionFrustumGroupBounds(group);
+		S32 res = AABBInRegionFrustumGroupBounds(group);
 		
-		S32 res = AABBInRegionFrustumNoFarClipGroupBounds(group);
+		//S32 res = AABBInRegionFrustumNoFarClipGroupBounds(group);
 		if (res != 0)
 		{
 			res = llmin(res, AABBRegionSphereIntersectGroupExtents(group, mLocalShift));
@@ -414,9 +435,9 @@ class LLVOCacheOctreeCull : public LLViewerOctreeCull
 
 	virtual S32 frustumCheckObjects(const LLviewerOctreeGroup* group)
 	{
-		//S32 res = AABBInRegionFrustumObjectBounds(group);
+		S32 res = AABBInRegionFrustumObjectBounds(group);
 
-		S32 res = AABBInRegionFrustumNoFarClipObjectBounds(group);
+		//S32 res = AABBInRegionFrustumNoFarClipObjectBounds(group);
 		if (res != 0)
 		{
 			res = llmin(res, AABBRegionSphereIntersectObjectExtents(group, mLocalShift));
@@ -426,7 +447,15 @@ class LLVOCacheOctreeCull : public LLViewerOctreeCull
 
 	virtual void processGroup(LLviewerOctreeGroup* base_group)
 	{
-		mRegionp->addVisibleGroup(base_group);
+		LLOcclusionCullingGroup* group = (LLOcclusionCullingGroup*)base_group;
+		if (group->needsUpdate() || group->mVisible[LLViewerCamera::sCurCameraID] < LLDrawable::getCurrentFrame() - 1)
+		{
+			((LLOcclusionCullingGroup*)group)->doOcclusion(mCamera);
+			group->setVisible();
+			return; //wait for occlusion culling results
+		}
+
+		mRegionp->addVisibleGroup(group);
 	}
 
 private:
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 12f268d3243..d1c16024378 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -4190,7 +4190,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 	
 	LLVOAvatar* pAvatarVO = NULL;
 
-	LLSpatialBridge* bridge = group->mSpatialPartition->asBridge();
+	LLSpatialBridge* bridge = group->getSpatialPartition()->asBridge();
 	if (bridge)
 	{
 		if (bridge->mAvatar.isNull())
@@ -4227,10 +4227,10 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 	std::vector<LLFace*> simple_faces;
 
 	std::vector<LLFace*> alpha_faces;
-	U32 useage = group->mSpatialPartition->mBufferUsage;
+	U32 useage = group->getSpatialPartition()->mBufferUsage;
 
-	U32 max_vertices = (gSavedSettings.getS32("RenderMaxVBOSize")*1024)/LLVertexBuffer::calcVertexSize(group->mSpatialPartition->mVertexDataMask);
-	U32 max_total = (gSavedSettings.getS32("RenderMaxNodeSize")*1024)/LLVertexBuffer::calcVertexSize(group->mSpatialPartition->mVertexDataMask);
+	U32 max_vertices = (gSavedSettings.getS32("RenderMaxVBOSize")*1024)/LLVertexBuffer::calcVertexSize(group->getSpatialPartition()->mVertexDataMask);
+	U32 max_total = (gSavedSettings.getS32("RenderMaxNodeSize")*1024)/LLVertexBuffer::calcVertexSize(group->getSpatialPartition()->mVertexDataMask);
 	max_vertices = llmin(max_vertices, (U32) 65535);
 
 	U32 cur_total = 0;
@@ -4837,7 +4837,7 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::
 #endif
 	
 	//calculate maximum number of vertices to store in a single buffer
-	U32 max_vertices = (gSavedSettings.getS32("RenderMaxVBOSize")*1024)/LLVertexBuffer::calcVertexSize(group->mSpatialPartition->mVertexDataMask);
+	U32 max_vertices = (gSavedSettings.getS32("RenderMaxVBOSize")*1024)/LLVertexBuffer::calcVertexSize(group->getSpatialPartition()->mVertexDataMask);
 	max_vertices = llmin(max_vertices, (U32) 65535);
 
 	{
@@ -5228,7 +5228,7 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::
 void LLGeometryManager::addGeometryCount(LLSpatialGroup* group, U32 &vertex_count, U32 &index_count)
 {	
 	//initialize to default usage for this partition
-	U32 usage = group->mSpatialPartition->mBufferUsage;
+	U32 usage = group->getSpatialPartition()->mBufferUsage;
 	
 	//clear off any old faces
 	mFaceList.clear();
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 1b5148e560b..a8156b46ed3 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -1663,7 +1663,7 @@ void LLPipeline::unlinkDrawable(LLDrawable *drawable)
 	if (drawablep->getSpatialGroup())
 	{
 		LLFastTimer t(FTM_REMOVE_FROM_SPATIAL_PARTITION);
-		if (!drawablep->getSpatialGroup()->mSpatialPartition->remove(drawablep, drawablep->getSpatialGroup()))
+		if (!drawablep->getSpatialGroup()->getSpatialPartition()->remove(drawablep, drawablep->getSpatialGroup()))
 		{
 #ifdef LL_RELEASE_FOR_DOWNLOAD
 			llwarns << "Couldn't remove object from spatial group!" << llendl;
@@ -2480,7 +2480,7 @@ void LLPipeline::markNotCulled(LLSpatialGroup* group, LLCamera& camera)
 
 	assertInitialized();
 	
-	if (!group->mSpatialPartition->mRenderByGroup)
+	if (!group->getSpatialPartition()->mRenderByGroup)
 	{ //render by drawable
 		sCull->pushDrawableGroup(group);
 	}
@@ -2729,7 +2729,7 @@ void LLPipeline::rebuildGroups()
 		{
 			group->rebuildGeom();
 			
-			if (group->mSpatialPartition->mRenderByGroup)
+			if (group->getSpatialPartition()->mRenderByGroup)
 			{
 				count++;
 			}
@@ -3052,9 +3052,9 @@ void LLPipeline::markMeshDirty(LLSpatialGroup* group)
 
 void LLPipeline::markRebuild(LLSpatialGroup* group, BOOL priority)
 {
-	if (group && !group->isDead() && group->mSpatialPartition)
+	if (group && !group->isDead() && group->getSpatialPartition())
 	{
-		if (group->mSpatialPartition->mPartitionType == LLViewerRegion::PARTITION_HUD)
+		if (group->getSpatialPartition()->mPartitionType == LLViewerRegion::PARTITION_HUD)
 		{
 			priority = TRUE;
 		}
@@ -3631,7 +3631,7 @@ void LLPipeline::postSort(LLCamera& camera)
 			
 			if (alpha != group->mDrawMap.end())
 			{ //store alpha groups for sorting
-				LLSpatialBridge* bridge = group->mSpatialPartition->asBridge();
+				LLSpatialBridge* bridge = group->getSpatialPartition()->asBridge();
 				if (LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD)
 				{
 					if (bridge)
@@ -5202,7 +5202,7 @@ void LLPipeline::renderDebug()
 				continue;
 			}
 
-			LLSpatialBridge* bridge = group->mSpatialPartition->asBridge();
+			LLSpatialBridge* bridge = group->getSpatialPartition()->asBridge();
 
 			if (bridge && (!bridge->mDrawable || bridge->mDrawable->isDead()))
 			{
@@ -9990,7 +9990,7 @@ void LLPipeline::renderGroups(LLRenderPass* pass, U32 type, U32 mask, BOOL textu
 		LLSpatialGroup* group = *i;
 		if (!group->isDead() &&
 			(!sUseOcclusion || !group->isOcclusionState(LLSpatialGroup::OCCLUDED)) &&
-			gPipeline.hasRenderType(group->mSpatialPartition->mDrawableType) &&
+			gPipeline.hasRenderType(group->getSpatialPartition()->mDrawableType) &&
 			group->mDrawMap.find(type) != group->mDrawMap.end())
 		{
 			pass->renderGroup(group,type,mask,texture);
-- 
GitLab