Commits

si...@Simon-PC.lindenlab.com  committed d4bbdc1 Merge

Merge simon/viewmaster-merge for latest maint work

  • Participants
  • Parent commits 354eaf5, d8a8531

Comments (0)

Files changed (165)

File .hgtags

File contents unchanged.

File indra/cmake/00-Common.cmake

       /Oy-
       /Zc:wchar_t-
       /arch:SSE2
+      /fp:fast
       )
      
   # Are we using the crummy Visual Studio KDU build workaround?

File indra/llcharacter/llcharacter.cpp

 //-----------------------------------------------------------------------------
 static LLFastTimer::DeclareTimer FTM_UPDATE_ANIMATION("Update Animation");
 static LLFastTimer::DeclareTimer FTM_UPDATE_HIDDEN_ANIMATION("Update Hidden Anim");
+static LLFastTimer::DeclareTimer FTM_UPDATE_MOTIONS("Update Motions");
 
 void LLCharacter::updateMotions(e_update_t update_type)
 {
 			mMotionController.unpauseAllMotions();
 		}
 		bool force_update = (update_type == FORCE_UPDATE);
-		mMotionController.updateMotions(force_update);
+		{
+			LLFastTimer t(FTM_UPDATE_MOTIONS);
+			mMotionController.updateMotions(force_update);
+		}
 	}
 }
 

File indra/llcharacter/lleditingmotion.cpp

File contents unchanged.

File indra/llcharacter/llkeyframewalkmotion.cpp

File contents unchanged.

File indra/llcharacter/llmotioncontroller.cpp

 //-----------------------------------------------------------------------------
 // updateMotionsByType()
 //-----------------------------------------------------------------------------
+static LLFastTimer::DeclareTimer FTM_MOTION_ON_UPDATE("Motion onUpdate");
+
 void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_type)
 {
 	BOOL update_result = TRUE;
 			}
 
 			// perform motion update
-			update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature);
+			{
+				LLFastTimer t(FTM_MOTION_ON_UPDATE);
+				update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature);
+			}
 		}
 
 		//**********************
 
 	// Always cap the number of loaded motions
 	purgeExcessMotions();
-	
+		
 	// Update timing info for this time step.
 	if (!mPaused)
 	{
 				}
 
 				updateLoadingMotions();
+				
 				return;
 			}
 			
 	}
 
 	updateLoadingMotions();
-
+	
 	resetJointSignatures();
 
 	if (mPaused && !force_update)
 	{
 		// update additive motions
 		updateAdditiveMotions();
+				
 		resetJointSignatures();
-
+		
 		// update all regular motions
 		updateRegularMotions();
-
+		
 		if (use_quantum)
 		{
 			mPoseBlender.blendAndCache(TRUE);

File indra/llcommon/llsd.cpp

 		virtual LLSD::UUID		asUUID() const	{ return LLUUID(mValue); }
 		virtual LLSD::Date		asDate() const	{ return LLDate(mValue); }
 		virtual LLSD::URI		asURI() const	{ return LLURI(mValue); }
+		virtual int				size() const	{ return mValue.size(); }
 	};
 	
 	LLSD::Integer	ImplString::asInteger() const

File indra/llcommon/llstrider.h

 	const LLStrider<Object>& operator =  (Object *first)    { mObjectp = first; return *this;}
 	void setStride (S32 skipBytes)	{ mSkip = (skipBytes ? skipBytes : sizeof(Object));}
 
+	LLStrider<Object> operator+(const S32& index) 
+	{
+		LLStrider<Object> ret;
+		ret.mBytep = mBytep + mSkip*index;
+		ret.mSkip = mSkip;
+
+		return ret;
+	}
+
 	void skip(const U32 index)     { mBytep += mSkip*index;}
 	U32 getSkip() const			   { return mSkip; }
 	Object* get()                  { return mObjectp; }
 	Object& operator *()           { return *mObjectp; }
 	Object* operator ++(int)       { Object* old = mObjectp; mBytep += mSkip; return old; }
 	Object* operator +=(int i)     { mBytep += mSkip*i; return mObjectp; }
+
 	Object& operator[](U32 index)  { return *(Object*)(mBytep + (mSkip * index)); }
 };
 

File indra/llmath/llcoord.h

File contents unchanged.

File indra/llmath/lloctree.h

 #include "v3math.h"
 #include "llvector4a.h"
 #include <vector>
-#include <set>
 
 #define OCT_ERRS LL_WARNS("OctreeErrors")
 
 
 	typedef LLOctreeTraveler<T>									oct_traveler;
 	typedef LLTreeTraveler<T>									tree_traveler;
-	typedef typename std::set<LLPointer<T> >					element_list;
-	typedef typename element_list::iterator						element_iter;
-	typedef typename element_list::const_iterator	const_element_iter;
+	typedef LLPointer<T>*										element_list;
+	typedef LLPointer<T>*										element_iter;
+	typedef const LLPointer<T>*									const_element_iter;
 	typedef typename std::vector<LLTreeListener<T>*>::iterator	tree_listener_iter;
-	typedef typename std::vector<LLOctreeNode<T>* >				child_list;
+	typedef LLOctreeNode<T>**									child_list;
+	typedef LLOctreeNode<T>**									child_iter;
+
 	typedef LLTreeNode<T>		BaseType;
 	typedef LLOctreeNode<T>		oct_node;
 	typedef LLOctreeListener<T>	oct_listener;
 	:	mParent((oct_node*)parent), 
 		mOctant(octant) 
 	{ 
+		mData = NULL;
+		mDataEnd = NULL;
+
 		mCenter = center;
 		mSize = size;
 
 	{ 
 		BaseType::destroyListeners(); 
 		
+		for (U32 i = 0; i < mElementCount; ++i)
+		{
+			mData[i]->setBinIndex(-1);
+			mData[i] = NULL;
+		}
+
+		free(mData);
+		mData = NULL;
+		mDataEnd = NULL;
+
 		for (U32 i = 0; i < getChildCount(); i++)
 		{
 			delete getChild(i);
 	}
 
 	void accept(oct_traveler* visitor)				{ visitor->visit(this); }
-	virtual bool isLeaf() const						{ return mChild.empty(); }
+	virtual bool isLeaf() const						{ return mChildCount == 0; }
 	
 	U32 getElementCount() const						{ return mElementCount; }
+	bool isEmpty() const							{ return mElementCount == 0; }
 	element_list& getData()							{ return mData; }
 	const element_list& getData() const				{ return mData; }
-	
+	element_iter getDataBegin()						{ return mData; }
+	element_iter getDataEnd()						{ return mDataEnd; }
+	const_element_iter getDataBegin() const			{ return mData; }
+	const_element_iter getDataEnd() const			{ return mDataEnd; }
+		
 	U32 getChildCount()	const						{ return mChildCount; }
 	oct_node* getChild(U32 index)					{ return mChild[index]; }
 	const oct_node* getChild(U32 index) const		{ return mChild[index]; }
 	
 	virtual bool insert(T* data)
 	{
-		if (data == NULL)
+		if (data == NULL || data->getBinIndex() != -1)
 		{
 			OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << llendl;
 			return false;
 			if ((getElementCount() < gOctreeMaxCapacity && contains(data->getBinRadius()) ||
 				(data->getBinRadius() > getSize()[0] &&	parent && parent->getElementCount() >= gOctreeMaxCapacity))) 
 			{ //it belongs here
-				//if this is a redundant insertion, error out (should never happen)
-				llassert(mData.find(data) == mData.end());
+				mElementCount++;
+				mData = (element_list) realloc(mData, sizeof(LLPointer<T>)*mElementCount);
 
-				mData.insert(data);
+				//avoid unref on uninitialized memory
+				memset(mData+mElementCount-1, 0, sizeof(LLPointer<T>));
+
+				mData[mElementCount-1] = data;
+				mDataEnd = mData + mElementCount;
+				data->setBinIndex(mElementCount-1);
 				BaseType::insert(data);
-
-				mElementCount = mData.size();
 				return true;
 			}
 			else
 
 				if( lt == 0x7 )
 				{
-					mData.insert(data);
+					mElementCount++;
+					mData = (element_list) realloc(mData, sizeof(LLPointer<T>)*mElementCount);
+
+					//avoid unref on uninitialized memory
+					memset(mData+mElementCount-1, 0, sizeof(LLPointer<T>));
+
+					mData[mElementCount-1] = data;
+					mDataEnd = mData + mElementCount;
+					data->setBinIndex(mElementCount-1);
 					BaseType::insert(data);
-
-					mElementCount = mData.size();
 					return true;
 				}
 
 		return false;
 	}
 
+	void _remove(T* data, S32 i)
+	{ //precondition -- mElementCount > 0, idx is in range [0, mElementCount)
+
+		mElementCount--;
+		data->setBinIndex(-1); 
+		
+		if (mElementCount > 0)
+		{
+			if (mElementCount != i)
+			{
+				mData[i] = mData[mElementCount]; //might unref data, do not access data after this point
+				mData[i]->setBinIndex(i);
+			}
+
+			mData[mElementCount] = NULL; //needed for unref
+			mData = (element_list) realloc(mData, sizeof(LLPointer<T>)*mElementCount);
+			mDataEnd = mData+mElementCount;
+		}
+		else
+		{
+			mData[0] = NULL; //needed for unref
+			free(mData);
+			mData = NULL;
+			mDataEnd = NULL;
+		}
+
+		notifyRemoval(data);
+		checkAlive();
+	}
+
 	bool remove(T* data)
 	{
-		if (mData.find(data) != mData.end())
-		{	//we have data
-			mData.erase(data);
-			mElementCount = mData.size();
-			notifyRemoval(data);
-			checkAlive();
-			return true;
+		S32 i = data->getBinIndex();
+
+		if (i >= 0 && i < mElementCount)
+		{
+			if (mData[i] == data)
+			{ //found it
+				_remove(data, i);
+				llassert(data->getBinIndex() == -1);
+				return true;
+			}
 		}
-		else if (isInside(data))
+		
+		if (isInside(data))
 		{
 			oct_node* dest = getNodeAt(data);
 
 			if (dest != this)
 			{
-				return dest->remove(data);
+				bool ret = dest->remove(data);
+				llassert(data->getBinIndex() == -1);
+				return ret;
 			}
 		}
 
 		//node is now root
 		llwarns << "!!! OCTREE REMOVING FACE BY ADDRESS, SEVERE PERFORMANCE PENALTY |||" << llendl;
 		node->removeByAddress(data);
+		llassert(data->getBinIndex() == -1);
 		return true;
 	}
 
 	void removeByAddress(T* data)
 	{
-        if (mData.find(data) != mData.end())
+        for (U32 i = 0; i < mElementCount; ++i)
 		{
-			mData.erase(data);
-			mElementCount = mData.size();
-			notifyRemoval(data);
-			llwarns << "FOUND!" << llendl;
-			checkAlive();
-			return;
+			if (mData[i] == data)
+			{ //we have data
+				_remove(data, i);
+				llwarns << "FOUND!" << llendl;
+				return;
+			}
 		}
 		
 		for (U32 i = 0; i < getChildCount(); i++)
 
 	void clearChildren()
 	{
-		mChild.clear();
 		mChildCount = 0;
+
 		U32* foo = (U32*) mChildMap;
 		foo[0] = foo[1] = 0xFFFFFFFF;
 	}
 
 		mChildMap[child->getOctant()] = mChildCount;
 
-		mChild.push_back(child);
+		mChild[mChildCount] = child;
 		++mChildCount;
 		child->setParent(this);
 
 			mChild[index]->destroy();
 			delete mChild[index];
 		}
-		mChild.erase(mChild.begin() + index);
+
 		--mChildCount;
 
+		mChild[index] = mChild[mChildCount];
+		
+
 		//rebuild child map
 		U32* foo = (U32*) mChildMap;
 		foo[0] = foo[1] = 0xFFFFFFFF;
 	oct_node* mParent;
 	U8 mOctant;
 
-	child_list mChild;
+	LLOctreeNode<T>* mChild[8];
 	U8 mChildMap[8];
 	U32 mChildCount;
 
 	element_list mData;
+	element_iter mDataEnd;
 	U32 mElementCount;
 		
 }; 

File indra/llmath/llvolume.cpp

 		LLVector4a& min = node->mExtents[0];
 		LLVector4a& max = node->mExtents[1];
 
-		if (!branch->getData().empty())
+		if (!branch->isEmpty())
 		{ //node has data, find AABB that binds data set
-			const LLVolumeTriangle* tri = *(branch->getData().begin());
+			const LLVolumeTriangle* tri = *(branch->getDataBegin());
 			
 			//initialize min/max to first available vertex
 			min = *(tri->mV[0]);
 			max = *(tri->mV[0]);
 			
 			for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = 
-				branch->getData().begin(); iter != branch->getData().end(); ++iter)
+				branch->getDataBegin(); iter != branch->getDataEnd(); ++iter)
 			{ //for each triangle in node
 
 				//stretch by triangles in node
 				max.setMax(max, *tri->mV[2]);
 			}
 		}
-		else if (!branch->getChildren().empty())
+		else if (!branch->isLeaf())
 		{ //no data, but child nodes exist
 			LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(0)->getListener(0);
 

File indra/llmath/llvolume.h

 #include "llstrider.h"
 #include "v4coloru.h"
 #include "llrefcount.h"
+#include "llpointer.h"
 #include "llfile.h"
 
 //============================================================================
 	LLVector2*  mTexCoords;
 	U16* mIndices;
 
+	//vertex buffer filled in by LLFace to cache this volume face geometry in vram 
+	// (declared as a LLPointer to LLRefCount to avoid dependency on LLVertexBuffer)
+	mutable LLPointer<LLRefCount> mVertexBuffer; 
+
 	std::vector<S32>	mEdge;
 
 	//list of skin weights for rigged volumes

File indra/llmath/llvolumeoctree.cpp

 void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle>* node)
 {
 	for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = 
-			node->getData().begin(); iter != node->getData().end(); ++iter)
+			node->getDataBegin(); iter != node->getDataEnd(); ++iter)
 	{
 		const LLVolumeTriangle* tri = *iter;
 
 	}
 
 	//children fit, check data
-	for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = branch->getData().begin(); 
-			iter != branch->getData().end(); ++iter)
+	for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = branch->getDataBegin(); 
+			iter != branch->getDataEnd(); ++iter)
 	{
 		const LLVolumeTriangle* tri = *iter;
 

File indra/llmath/llvolumeoctree.h

 public:
 	LLVolumeTriangle()
 	{
-		
+		mBinIndex = -1;	
 	}
 
 	LLVolumeTriangle(const LLVolumeTriangle& rhs)
 	U16 mIndex[3];
 
 	F32 mRadius;
+	mutable S32 mBinIndex;
+
 
 	virtual const LLVector4a& getPositionGroup() const;
 	virtual const F32& getBinRadius() const;
+	
+	S32 getBinIndex() const { return mBinIndex; }
+	void setBinIndex(S32 idx) const { mBinIndex = idx; }
+
+
 };
 
 class LLVolumeOctreeListener : public LLOctreeListener<LLVolumeTriangle>

File indra/llmessage/llsdmessagereader.cpp

 	case LLSD::TypeReal:
 		return sizeof(F64);
 	case LLSD::TypeString:
-		return llsd.asString().size();
+		return llsd.size();
 	case LLSD::TypeUUID:
 		return sizeof(LLUUID);
 	case LLSD::TypeDate:

File indra/llrender/llcubemap.cpp

 		{
 			U32 texname = 0;
 			
-			LLImageGL::generateTextures(1, &texname);
+			LLImageGL::generateTextures(LLTexUnit::TT_CUBE_MAP, GL_RGB8, 1, &texname);
 
 			for (int i = 0; i < 6; i++)
 			{

File indra/llrender/llgl.cpp

 PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv = NULL;
 PFNGLSAMPLEMASKIPROC glSampleMaski = NULL;
 
+//transform feedback (4.0 core)
+PFNGLBEGINTRANSFORMFEEDBACKPROC glBeginTransformFeedback = NULL;
+PFNGLENDTRANSFORMFEEDBACKPROC glEndTransformFeedback = NULL;
+PFNGLTRANSFORMFEEDBACKVARYINGSPROC glTransformFeedbackVaryings = NULL;
+PFNGLBINDBUFFERRANGEPROC glBindBufferRange = NULL;
+
 //GL_ARB_debug_output
 PFNGLDEBUGMESSAGECONTROLARBPROC glDebugMessageControlARB = NULL;
 PFNGLDEBUGMESSAGEINSERTARBPROC glDebugMessageInsertARB = NULL;
 	mHasDrawBuffers(FALSE),
 	mHasTextureRectangle(FALSE),
 	mHasTextureMultisample(FALSE),
+	mHasTransformFeedback(FALSE),
 	mMaxSampleMaskWords(0),
 	mMaxColorTextureSamples(0),
 	mMaxDepthTextureSamples(0),
 	parse_gl_version( &mDriverVersionMajor, 
 		&mDriverVersionMinor, 
 		&mDriverVersionRelease, 
-		&mDriverVersionVendorString );
+		&mDriverVersionVendorString,
+		&mGLVersionString);
 
 	mGLVersion = mDriverVersionMajor + mDriverVersionMinor * .1f;
 
 	mHasMultitexture = glh_init_extensions("GL_ARB_multitexture");
 	mHasATIMemInfo = ExtensionExists("GL_ATI_meminfo", gGLHExts.mSysExts);
 	mHasNVXMemInfo = ExtensionExists("GL_NVX_gpu_memory_info", gGLHExts.mSysExts);
-	mHasMipMapGeneration = glh_init_extensions("GL_SGIS_generate_mipmap");
 	mHasSeparateSpecularColor = glh_init_extensions("GL_EXT_separate_specular_color");
 	mHasAnisotropic = glh_init_extensions("GL_EXT_texture_filter_anisotropic");
 	glh_init_extensions("GL_ARB_texture_cube_map");
 							ExtensionExists("GL_EXT_packed_depth_stencil", gGLHExts.mSysExts);
 #endif
 	
+	mHasMipMapGeneration = mHasFramebufferObject || mGLVersion >= 1.4f;
+
 	mHasDrawBuffers = ExtensionExists("GL_ARB_draw_buffers", gGLHExts.mSysExts);
 	mHasBlendFuncSeparate = ExtensionExists("GL_EXT_blend_func_separate", gGLHExts.mSysExts);
 	mHasTextureRectangle = ExtensionExists("GL_ARB_texture_rectangle", gGLHExts.mSysExts);
 	mHasTextureMultisample = ExtensionExists("GL_ARB_texture_multisample", gGLHExts.mSysExts);
 	mHasDebugOutput = ExtensionExists("GL_ARB_debug_output", gGLHExts.mSysExts);
+	mHasTransformFeedback = mGLVersion >= 4.f ? TRUE : FALSE;
 #if !LL_DARWIN
 	mHasPointParameters = !mIsATI && ExtensionExists("GL_ARB_point_parameters", gGLHExts.mSysExts);
 #endif
 		glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC) GLH_EXT_GET_PROC_ADDRESS("glTexImage3DMultisample");
 		glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC) GLH_EXT_GET_PROC_ADDRESS("glGetMultisamplefv");
 		glSampleMaski = (PFNGLSAMPLEMASKIPROC) GLH_EXT_GET_PROC_ADDRESS("glSampleMaski");
-	}	
+	}
+	if (mHasTransformFeedback)
+	{
+		glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC) GLH_EXT_GET_PROC_ADDRESS("glBeginTransformFeedback");
+		glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC) GLH_EXT_GET_PROC_ADDRESS("glEndTransformFeedback");
+		glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC) GLH_EXT_GET_PROC_ADDRESS("glTransformFeedbackVaryings");
+		glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) GLH_EXT_GET_PROC_ADDRESS("glBindBufferRange");
+	}
 	if (mHasDebugOutput)
 	{
 		glDebugMessageControlARB = (PFNGLDEBUGMESSAGECONTROLARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDebugMessageControlARB");
 			case GL_COLOR_MATERIAL:
 			case GL_FOG:
 			case GL_LINE_STIPPLE:
+			case GL_POLYGON_STIPPLE:
 				mState = 0;
 				break;
 		}
 
 ////////////////////////////////////////////////////////////////////////////////
 
-void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific )
+void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific, std::string* version_string )
 {
 	// GL_VERSION returns a null-terminated string with the format: 
 	// <major>.<minor>[.<release>] [<vendor specific>]
 		return;
 	}
 
+	version_string->assign(version);
+
 	std::string ver_copy( version );
 	S32 len = (S32)strlen( version );	/* Flawfinder: ignore */
 	S32 i = 0;
 	gGL.matrixMode(LLRender::MM_MODELVIEW);
 }
 
+
+	
+LLGLSyncFence::LLGLSyncFence()
+{
+#ifdef GL_ARB_sync
+	mSync = 0;
+#endif
+}
+
+LLGLSyncFence::~LLGLSyncFence()
+{
+#ifdef GL_ARB_sync
+	if (mSync)
+	{
+		glDeleteSync(mSync);
+	}
+#endif
+}
+
+void LLGLSyncFence::placeFence()
+{
+#ifdef GL_ARB_sync
+	if (mSync)
+	{
+		glDeleteSync(mSync);
+	}
+	mSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+#endif
+}
+
+bool LLGLSyncFence::isCompleted()
+{
+	bool ret = true;
+#ifdef GL_ARB_sync
+	if (mSync)
+	{
+		GLenum status = glClientWaitSync(mSync, 0, 1);
+		if (status == GL_TIMEOUT_EXPIRED)
+		{
+			ret = false;
+		}
+	}
+#endif
+	return ret;
+}
+
+void LLGLSyncFence::wait()
+{
+#ifdef GL_ARB_sync
+	if (mSync)
+	{
+		while (glClientWaitSync(mSync, 0, FENCE_WAIT_TIME_NANOSECONDS) == GL_TIMEOUT_EXPIRED)
+		{ //track the number of times we've waited here
+			static S32 waits = 0;
+			waits++;
+		}
+	}
+#endif
+}
+
+
+

File indra/llrender/llgl.h

 	BOOL mHasDepthClamp;
 	BOOL mHasTextureRectangle;
 	BOOL mHasTextureMultisample;
+	BOOL mHasTransformFeedback;
 	S32 mMaxSampleMaskWords;
 	S32 mMaxColorTextureSamples;
 	S32 mMaxDepthTextureSamples;
 	S32 mGLSLVersionMajor;
 	S32 mGLSLVersionMinor;
 	std::string mDriverVersionVendorString;
+	std::string mGLVersionString;
 
 	S32 mVRAM; // VRAM in MB
 	S32 mGLMaxVertexRange;
 	virtual void updateGL() = 0;
 };
 
+const U32 FENCE_WAIT_TIME_NANOSECONDS = 1000;  //1 ms
+
+class LLGLFence
+{
+public:
+	virtual void placeFence() = 0;
+	virtual bool isCompleted() = 0;
+	virtual void wait() = 0;
+};
+
+class LLGLSyncFence : public LLGLFence
+{
+public:
+#ifdef GL_ARB_sync
+	GLsync mSync;
+#endif
+	
+	LLGLSyncFence();
+	virtual ~LLGLSyncFence();
+
+	void placeFence();
+	bool isCompleted();
+	void wait();
+};
+
 extern LLMatrix4 gGLObliqueProjectionInverse;
 
 #include "llglstates.h"
 
 void init_glstates();
 
-void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific );
+void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific, std::string* version_string );
 
 extern BOOL gClothRipple;
 extern BOOL gHeadlessClient;

File indra/llrender/llglheaders.h

 extern PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv;
 extern PFNGLSAMPLEMASKIPROC glSampleMaski;
 
+//transform feedback (4.0 core)
+extern PFNGLBEGINTRANSFORMFEEDBACKPROC glBeginTransformFeedback;
+extern PFNGLENDTRANSFORMFEEDBACKPROC glEndTransformFeedback;
+extern PFNGLTRANSFORMFEEDBACKVARYINGSPROC glTransformFeedbackVaryings;
+extern PFNGLBINDBUFFERRANGEPROC glBindBufferRange;
+
+
 #elif LL_WINDOWS
 //----------------------------------------------------------------------------
 // LL_WINDOWS
 extern PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv;
 extern PFNGLSAMPLEMASKIPROC glSampleMaski;
 
+//transform feedback (4.0 core)
+extern PFNGLBEGINTRANSFORMFEEDBACKPROC glBeginTransformFeedback;
+extern PFNGLENDTRANSFORMFEEDBACKPROC glEndTransformFeedback;
+extern PFNGLTRANSFORMFEEDBACKVARYINGSPROC glTransformFeedbackVaryings;
+extern PFNGLBINDBUFFERRANGEPROC glBindBufferRange;
+
 //GL_ARB_debug_output
 extern PFNGLDEBUGMESSAGECONTROLARBPROC glDebugMessageControlARB;
 extern PFNGLDEBUGMESSAGEINSERTARBPROC glDebugMessageInsertARB;

File indra/llrender/llglslshader.cpp

 }
 
 BOOL LLGLSLShader::createShader(vector<string> * attributes,
-								vector<string> * uniforms)
+								vector<string> * uniforms,
+								U32 varying_count,
+								const char** varyings)
 {
 	//reloading, reset matrix hash values
 	for (U32 i = 0; i < LLRender::NUM_MATRIX_MODES; ++i)
 		mFeatures.mIndexedTextureChannels = llmin(mFeatures.mIndexedTextureChannels, 1);
 	}
 
+#ifdef GL_INTERLEAVED_ATTRIBS
+	if (varying_count > 0 && varyings)
+	{
+		glTransformFeedbackVaryings(mProgramObject, varying_count, varyings, GL_INTERLEAVED_ATTRIBS);
+	}
+#endif
+
 	// Map attributes and uniforms
 	if (success)
 	{

File indra/llrender/llglslshader.h

 
 	void unload();
 	BOOL createShader(std::vector<std::string> * attributes,
-						std::vector<std::string> * uniforms);
+						std::vector<std::string> * uniforms,
+						U32 varying_count = 0,
+						const char** varyings = NULL);
 	BOOL attachObject(std::string object);
 	void attachObject(GLhandleARB object);
 	void attachObjects(GLhandleARB* objects = NULL, S32 count = 0);

File indra/llrender/llimagegl.cpp

 //----------------------------------------------------------------------------
 const F32 MIN_TEXTURE_LIFETIME = 10.f;
 
+//which power of 2 is i?
+//assumes i is a power of 2 > 0
+U32 wpo2(U32 i);
+
 //statics
 
 U32 LLImageGL::sUniqueCount				= 0;
 S32 LLImageGL::sBoundTextureMemoryInBytes		= 0;
 S32 LLImageGL::sCurBoundTextureMemory	= 0;
 S32 LLImageGL::sCount					= 0;
-std::list<U32> LLImageGL::sDeadTextureList;
+LLImageGL::dead_texturelist_t LLImageGL::sDeadTextureList[LLTexUnit::TT_NONE];
+U32 LLImageGL::sCurTexName = 1;
 
 BOOL LLImageGL::sGlobalUseAnisotropic	= FALSE;
 F32 LLImageGL::sLastFrameTime			= 0.f;
 
 //----------------------------------------------------------------------------
 
+static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE_STATS("Image Stats");
 // static
 void LLImageGL::updateStats(F32 current_time)
 {
+	LLFastTimer t(FTM_IMAGE_UPDATE_STATS);
 	sLastFrameTime = current_time;
 	sBoundTextureMemoryInBytes = sCurBoundTextureMemory;
 	sCurBoundTextureMemory = 0;
 	mTarget = GL_TEXTURE_2D;
 	mBindTarget = LLTexUnit::TT_TEXTURE;
 	mHasMipMaps = false;
+	mMipLevels = -1;
 
 	mIsResident = 0;
 
 		is_compressed = true;
 	}
 
+	
+	
+	if (mUseMipMaps)
+	{
+		//set has mip maps to true before binding image so tex parameters get set properly
+		gGL.getTexUnit(0)->unbind(mBindTarget);
+		mHasMipMaps = true;
+		mTexOptionsDirty = true;
+		setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
+	}
+	else
+	{
+		mHasMipMaps = false;
+	}
+	
 	llverify(gGL.getTexUnit(0)->bind(this));
 	
+	
 	if (mUseMipMaps)
 	{
 		if (data_hasmips)
 				S32 w = getWidth(d);
 				S32 h = getHeight(d);
 				S32 gl_level = d-mCurrentDiscardLevel;
+
+				mMipLevels = llmax(mMipLevels, gl_level);
+
 				if (d > mCurrentDiscardLevel)
 				{
 					data_in -= dataFormatBytes(mFormatPrimary, w, h); // see above comment
 		{
 			if (mAutoGenMips)
 			{
-				if (!gGLManager.mHasFramebufferObject)
-				{
-					glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_GENERATE_MIPMAP_SGIS, TRUE);
-				}
 				stop_glerror();
 				{
 // 					LLFastTimer t2(FTM_TEMP4);
 					S32 w = getWidth(mCurrentDiscardLevel);
 					S32 h = getHeight(mCurrentDiscardLevel);
 
+					mMipLevels = wpo2(llmax(w, h));
+
+					//use legacy mipmap generation mode
+					glTexParameteri(mTarget, GL_GENERATE_MIPMAP, GL_TRUE);
+					
 					LLImageGL::setManualImage(mTarget, 0, mFormatInternal,
 								 w, h, 
 								 mFormatPrimary, mFormatType,
 						stop_glerror();
 					}
 				}
-
-				if (gGLManager.mHasFramebufferObject)
-				{
-					glGenerateMipmap(LLTexUnit::getInternalType(mBindTarget));
-				}
 			}
 			else
 			{
 				// Create mips by hand
-				// about 30% faster than autogen on ATI 9800, 50% slower on nVidia 4800
 				// ~4x faster than gluBuild2DMipmaps
 				S32 width = getWidth(mCurrentDiscardLevel);
 				S32 height = getHeight(mCurrentDiscardLevel);
 				const U8* cur_mip_data = 0;
 				S32 prev_mip_size = 0;
 				S32 cur_mip_size = 0;
+				
+				mMipLevels = nummips;
+
 				for (int m=0; m<nummips; m++)
 				{
 					if (m==0)
 		{
 			llerrs << "Compressed Image has mipmaps but data does not (can not auto generate compressed mips)" << llendl;
 		}
-		mHasMipMaps = true;
 	}
 	else
 	{
+		mMipLevels = 0;
 		S32 w = getWidth();
 		S32 h = getHeight();
 		if (is_compressed)
 			}
 
 		}
-		mHasMipMaps = false;
 	}
 	stop_glerror();
 	mGLTextureCreated = true;
 }
 
 // static
-void LLImageGL::generateTextures(S32 numTextures, U32 *textures)
+void LLImageGL::generateTextures(LLTexUnit::eTextureType type, U32 format, S32 numTextures, U32 *textures)
 {
-	glGenTextures(numTextures, (GLuint*)textures);
+	bool empty = true;
+
+	dead_texturelist_t::iterator iter = sDeadTextureList[type].find(format);
+	
+	if (iter != sDeadTextureList[type].end())
+	{
+		empty = iter->second.empty();
+	}
+	
+	for (S32 i = 0; i < numTextures; ++i)
+	{
+		if (!empty)
+		{
+			textures[i] = iter->second.front();
+			iter->second.pop_front();
+			empty = iter->second.empty();
+		}
+		else
+		{
+			textures[i] = sCurTexName++;
+		}
+	}
 }
 
 // static
-void LLImageGL::deleteTextures(S32 numTextures, U32 *textures, bool immediate)
+void LLImageGL::deleteTextures(LLTexUnit::eTextureType type, U32 format, S32 mip_levels, S32 numTextures, U32 *textures, bool immediate)
 {
-	for (S32 i = 0; i < numTextures; i++)
+	if (gGLManager.mInited)
 	{
-		sDeadTextureList.push_back(textures[i]);
+		if (format == 0 ||  type == LLTexUnit::TT_CUBE_MAP || mip_levels == -1)
+		{ //unknown internal format or unknown number of mip levels, not safe to reuse
+			glDeleteTextures(numTextures, textures);
+		}
+		else
+		{
+			for (S32 i = 0; i < numTextures; ++i)
+			{ //remove texture from VRAM by setting its size to zero
+				for (S32 j = 0; j <= mip_levels; j++)
+				{
+					gGL.getTexUnit(0)->bindManual(type, textures[i]);
+
+					glTexImage2D(LLTexUnit::getInternalType(type), j, format, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+				}
+
+				llassert(std::find(sDeadTextureList[type][format].begin(),
+								   sDeadTextureList[type][format].end(), textures[i]) == 
+								   sDeadTextureList[type][format].end());
+
+				sDeadTextureList[type][format].push_back(textures[i]);
+			}	
+		}
 	}
-
-	if (immediate)
+	
+	/*if (immediate)
 	{
 		LLImageGL::deleteDeadTextures();
-	}
+	}*/
 }
 
 // static
 
 	if(mTexName)
 	{
-		glDeleteTextures(1, (reinterpret_cast<GLuint*>(&mTexName))) ;
+		LLImageGL::deleteTextures(mBindTarget, mFormatInternal, mMipLevels, 1, (reinterpret_cast<GLuint*>(&mTexName))) ;
 	}
 	
-	glGenTextures(1, (GLuint*)&mTexName);
+
+	LLImageGL::generateTextures(mBindTarget, mFormatInternal, 1, &mTexName);
 	stop_glerror();
 	if (!mTexName)
 	{
 	}
 	else
 	{
-		LLImageGL::generateTextures(1, &mTexName);
+		LLImageGL::generateTextures(mBindTarget, mFormatInternal, 1, &mTexName);
 		stop_glerror();
 		{
 			llverify(gGL.getTexUnit(0)->bind(this));
 	{
 		sGlobalTextureMemoryInBytes -= mTextureMemory;
 
-		LLImageGL::deleteTextures(1, &old_name);
+		LLImageGL::deleteTextures(mBindTarget, mFormatInternal, mMipLevels, 1, &old_name);
 
 		stop_glerror();
 	}
 {
 	bool reset = false;
 
-	while (!sDeadTextureList.empty())
+	/*while (!sDeadTextureList.empty())
 	{
 		GLuint tex = sDeadTextureList.front();
 		sDeadTextureList.pop_front();
 		
 		glDeleteTextures(1, &tex);
 		stop_glerror();
-	}
+	}*/
 
 	if (reset)
 	{
 			mTextureMemory = 0;
 		}
 		
-		LLImageGL::deleteTextures(1, &mTexName);			
+		LLImageGL::deleteTextures(mBindTarget,  mFormatInternal, mMipLevels, 1, &mTexName);			
 		mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel.
 		mTexName = 0;		
 		mGLTextureCreated = FALSE ;

File indra/llrender/llimagegl.h

 {
 	friend class LLTexUnit;
 public:
-	static std::list<U32> sDeadTextureList;
+	static U32 sCurTexName;
 
+	//previously used but now available texture names
+	// sDeadTextureList[<usage>][<internal format>]
+	typedef std::map<U32, std::list<U32> > dead_texturelist_t;
+	static dead_texturelist_t sDeadTextureList[LLTexUnit::TT_NONE];
+
+	// These 2 functions replace glGenTextures() and glDeleteTextures()
+	static void generateTextures(LLTexUnit::eTextureType type, U32 format, S32 numTextures, U32 *textures);
+	static void deleteTextures(LLTexUnit::eTextureType type, U32 format, S32 mip_levels, S32 numTextures, U32 *textures, bool immediate = false);
 	static void deleteDeadTextures();
 
 	// Size calculation
 	void setComponents(S32 ncomponents) { mComponents = (S8)ncomponents ;}
 	void setAllowCompression(bool allow) { mAllowCompression = allow; }
 
-	// These 3 functions currently wrap glGenTextures(), glDeleteTextures(), and glTexImage2D() 
-	// for tracking purposes and will be deprecated in the future
-	static void generateTextures(S32 numTextures, U32 *textures);
-	static void deleteTextures(S32 numTextures, U32 *textures, bool immediate = false);
 	static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels, bool allow_compression = true);
 
 	BOOL createGLTexture() ;
 	LLGLenum mTarget;		// Normally GL_TEXTURE2D, sometimes something else (ex. cube maps)
 	LLTexUnit::eTextureType mBindTarget;	// Normally TT_TEXTURE, sometimes something else (ex. cube maps)
 	bool mHasMipMaps;
-	
+	S32 mMipLevels;
+
 	LLGLboolean mIsResident;
 	
 	S8 mComponents;

File indra/llrender/llrender.cpp

 
 	if (mIndex < 0) return;
 
+	//always flush and activate for consistency 
+	//   some code paths assume unbind always flushes and sets the active texture
+	gGL.flush();
+	activate();
+
 	// Disabled caching of binding state.
 	if (mCurrTexType == type)
 	{
-		gGL.flush();
-
-		activate();
 		mCurrTexture = 0;
 		if (LLGLSLShader::sNoFixedFunction && type == LLTexUnit::TT_TEXTURE)
 		{
 	} 
 	else if (option >= TFO_BILINEAR)
 	{
-		glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+		if (mHasMipMaps)
+		{
+			glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
+		}
+		else
+		{
+			glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+		}
 	}
 	else
 	{
-		glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+		if (mHasMipMaps)
+		{
+			glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
+		}
+		else
+		{
+			glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+		}
 	}
 
 	if (gGLManager.mHasAnisotropic)

File indra/llrender/llrendertarget.cpp

 LLRenderTarget::LLRenderTarget() :
 	mResX(0),
 	mResY(0),
-	mTex(0),
 	mFBO(0),
 	mDepth(0),
 	mStencil(0),
 	release();
 }
 
+void LLRenderTarget::resize(U32 resx, U32 resy, U32 color_fmt)
+{ 
+	//for accounting, get the number of pixels added/subtracted
+	S32 pix_diff = (resx*resy)-(mResX*mResY);
+		
+	mResX = resx;
+	mResY = resy;
+
+	for (U32 i = 0; i < mTex.size(); ++i)
+	{ //resize color attachments
+		gGL.getTexUnit(0)->bindManual(mUsage, mTex[i]);
+		LLImageGL::setManualImage(LLTexUnit::getInternalType(mUsage), 0, color_fmt, mResX, mResY, GL_RGBA, GL_UNSIGNED_BYTE, NULL, false);
+		sBytesAllocated += pix_diff*4;
+	}
+
+	if (mDepth)
+	{ //resize depth attachment
+		if (mStencil)
+		{
+			//use render buffers where stencil buffers are in play
+			glBindRenderbuffer(GL_RENDERBUFFER, mDepth);
+			glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mResX, mResY);
+			glBindRenderbuffer(GL_RENDERBUFFER, 0);
+		}
+		else
+		{
+			gGL.getTexUnit(0)->bindManual(mUsage, mDepth);
+			U32 internal_type = LLTexUnit::getInternalType(mUsage);
+			LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false);
+		}
+
+		sBytesAllocated += pix_diff*4;
+	}
+}
+	
+
 bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo, S32 samples)
 {
 	stop_glerror();
 	}
 
 	U32 tex;
-	LLImageGL::generateTextures(1, &tex);
+	LLImageGL::generateTextures(mUsage, color_fmt, 1, &tex);
 	gGL.getTexUnit(0)->bindManual(mUsage, tex);
 
 	stop_glerror();
 	}
 
 	mTex.push_back(tex);
+	mInternalFormat.push_back(color_fmt);
 
 	if (gDebugGL)
 	{ //bind and unbind to validate target
 	}
 	else
 	{
-		LLImageGL::generateTextures(1, &mDepth);
+		LLImageGL::generateTextures(mUsage, GL_DEPTH_COMPONENT24, 1, &mDepth);
 		gGL.getTexUnit(0)->bindManual(mUsage, mDepth);
 		
 		U32 internal_type = LLTexUnit::getInternalType(mUsage);
 		}
 		else
 		{
-			LLImageGL::deleteTextures(1, &mDepth, true);
+			LLImageGL::deleteTextures(mUsage, 0, 0, 1, &mDepth, true);
 			stop_glerror();
 		}
 		mDepth = 0;
 	if (mTex.size() > 0)
 	{
 		sBytesAllocated -= mResX*mResY*4*mTex.size();
-		LLImageGL::deleteTextures(mTex.size(), &mTex[0], true);
+		LLImageGL::deleteTextures(mUsage, mInternalFormat[0], 0, mTex.size(), &mTex[0], true);
 		mTex.clear();
+		mInternalFormat.clear();
 	}
 	
 	mResX = mResY = 0;

File indra/llrender/llrendertarget.h

 
 */
 
-class LLMultisampleBuffer;
-
 class LLRenderTarget
 {
 public:
 	//multiple calls will release previously allocated resources
 	bool allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage = LLTexUnit::TT_TEXTURE, bool use_fbo = false, S32 samples = 0);
 
+	//resize existing attachments to use new resolution and color format
+	// CAUTION: if the GL runs out of memory attempting to resize, this render target will be undefined
+	// DO NOT use for screen space buffers or for scratch space for an image that might be uploaded
+	// DO use for render targets that resize often and aren't likely to ruin someone's day if they break
+	void resize(U32 resx, U32 resy, U32 color_fmt);
+
 	//add color buffer attachment
 	//limit of 4 color attachments per render target
 	bool addColorAttachment(U32 color_fmt);
 	U32 mResX;
 	U32 mResY;
 	std::vector<U32> mTex;
+	std::vector<U32> mInternalFormat;
 	U32 mFBO;
 	U32 mDepth;
 	bool mStencil;

File indra/llrender/llshadermgr.cpp

 		text[count++] = strdup("#define textureCube texture\n");
 		text[count++] = strdup("#define texture2DLod textureLod\n");
 		text[count++] = strdup("#define	shadow2D(a,b) vec2(texture(a,b))\n");
-
+		
 		if (major_version > 1 || minor_version >= 40)
 		{ //GLSL 1.40 replaces texture2DRect et al with texture
 			text[count++] = strdup("#define texture2DRect texture\n");
 	mReservedUniforms.push_back("size");
 	mReservedUniforms.push_back("falloff");
 
+	mReservedUniforms.push_back("box_center");
+	mReservedUniforms.push_back("box_size");
+
 
 	mReservedUniforms.push_back("minLuminance");
 	mReservedUniforms.push_back("maxExtractAlpha");

File indra/llrender/llshadermgr.h

 		LIGHT_CENTER,
 		LIGHT_SIZE,
 		LIGHT_FALLOFF,
+		BOX_CENTER,
+		BOX_SIZE,
 
 		GLOW_MIN_LUMINANCE,
 		GLOW_MAX_EXTRACT_ALPHA,

File indra/llrender/llvertexbuffer.cpp

 #include "llglslshader.h"
 #include "llmemory.h"
 
-#if LL_DARWIN
-#define LL_VBO_POOLING 1
-#else
-#define LL_VBO_POOLING 0
-#endif
-
 //Next Highest Power Of Two
 //helper function, returns first number > v that is a power of 2, or v if v is already a power of 2
 U32 nhpo2(U32 v)
 
 
 const U32 LL_VBO_BLOCK_SIZE = 2048;
+const U32 LL_VBO_POOL_MAX_SEED_SIZE = 256*1024;
 
 U32 vbo_block_size(U32 size)
 { //what block size will fit size?
 	return vbo_block_size(size)/LL_VBO_BLOCK_SIZE;
 }
 
+const U32 LL_VBO_POOL_SEED_COUNT = vbo_block_index(LL_VBO_POOL_MAX_SEED_SIZE);
 
 
 //============================================================================
 
 U32 LLVBOPool::sBytesPooled = 0;
 U32 LLVBOPool::sIndexBytesPooled = 0;
+U32 LLVBOPool::sCurGLName = 1;
+
+std::list<U32> LLVertexBuffer::sAvailableVAOName;
+U32 LLVertexBuffer::sCurVAOName = 1;
+
 U32 LLVertexBuffer::sAllocatedIndexBytes = 0;
 U32 LLVertexBuffer::sIndexCount = 0;
 
 bool LLVertexBuffer::sUseVAO = false;
 bool LLVertexBuffer::sPreferStreamDraw = false;
 
-const U32 FENCE_WAIT_TIME_NANOSECONDS = 10000;  //1 ms
 
-class LLGLSyncFence : public LLGLFence
+U32 LLVBOPool::genBuffer()
 {
-public:
-#ifdef GL_ARB_sync
-	GLsync mSync;
-#endif
-	
-	LLGLSyncFence()
+	U32 ret = 0;
+
+	if (mGLNamePool.empty())
 	{
-#ifdef GL_ARB_sync
-		mSync = 0;
-#endif
+		ret = sCurGLName++;
+	}
+	else
+	{
+		ret = mGLNamePool.front();
+		mGLNamePool.pop_front();
 	}
 
-	virtual ~LLGLSyncFence()
+	return ret;
+}
+
+void LLVBOPool::deleteBuffer(U32 name)
+{
+	if (gGLManager.mInited)
 	{
-#ifdef GL_ARB_sync
-		if (mSync)
-		{
-			glDeleteSync(mSync);
-		}
-#endif
+		LLVertexBuffer::unbind();
+
+		glBindBufferARB(mType, name);
+		glBufferDataARB(mType, 0, NULL, mUsage);
+
+		llassert(std::find(mGLNamePool.begin(), mGLNamePool.end(), name) == mGLNamePool.end());
+
+		mGLNamePool.push_back(name);
+
+		glBindBufferARB(mType, 0);
 	}
+}
 
-	void placeFence()
-	{
-#ifdef GL_ARB_sync
-		if (mSync)
-		{
-			glDeleteSync(mSync);
-		}
-		mSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
-#endif
-	}
 
-	void wait()
-	{
-#ifdef GL_ARB_sync
-		if (mSync)
-		{
-			while (glClientWaitSync(mSync, 0, FENCE_WAIT_TIME_NANOSECONDS) == GL_TIMEOUT_EXPIRED)
-			{ //track the number of times we've waited here
-				static S32 waits = 0;
-				waits++;
-			}
-		}
-#endif
-	}
+LLVBOPool::LLVBOPool(U32 vboUsage, U32 vboType)
+: mUsage(vboUsage), mType(vboType)
+{
+	mMissCount.resize(LL_VBO_POOL_SEED_COUNT);
+	std::fill(mMissCount.begin(), mMissCount.end(), 0);
+}
 
-
-};
-
-
-volatile U8* LLVBOPool::allocate(U32& name, U32 size)
+volatile U8* LLVBOPool::allocate(U32& name, U32 size, bool for_seed)
 {
 	llassert(vbo_block_size(size) == size);
 	
 	volatile U8* ret = NULL;
 
-#if LL_VBO_POOLING
-
 	U32 i = vbo_block_index(size);
 
 	if (mFreeList.size() <= i)
 		mFreeList.resize(i+1);
 	}
 
-	if (mFreeList[i].empty())
+	if (mFreeList[i].empty() || for_seed)
 	{
 		//make a new buffer
-		glGenBuffersARB(1, &name);
+		name = genBuffer();
+		
 		glBindBufferARB(mType, name);
 
+		if (!for_seed && i < LL_VBO_POOL_SEED_COUNT)
+		{ //record this miss
+			mMissCount[i]++;	
+		}
+
 		if (mType == GL_ARRAY_BUFFER_ARB)
 		{
 			LLVertexBuffer::sAllocatedBytes += size;
 		}
 
 		glBindBufferARB(mType, 0);
+
+		if (for_seed)
+		{ //put into pool for future use
+			llassert(mFreeList.size() > i);
+
+			Record rec;
+			rec.mGLName = name;
+			rec.mClientData = ret;
+	
+			if (mType == GL_ARRAY_BUFFER_ARB)
+			{
+				sBytesPooled += size;
+			}
+			else
+			{
+				sIndexBytesPooled += size;
+			}
+			mFreeList[i].push_back(rec);
+		}
 	}
 	else
 	{
 
 		mFreeList[i].pop_front();
 	}
-#else //no pooling
-
-	glGenBuffersARB(1, &name);
-	glBindBufferARB(mType, name);
-
-	if (mType == GL_ARRAY_BUFFER_ARB)
-	{
-		LLVertexBuffer::sAllocatedBytes += size;
-	}
-	else
-	{
-		LLVertexBuffer::sAllocatedIndexBytes += size;
-	}
-
-	if (LLVertexBuffer::sDisableVBOMapping || mUsage != GL_DYNAMIC_DRAW_ARB)
-	{
-		glBufferDataARB(mType, size, 0, mUsage);
-		ret = (U8*) ll_aligned_malloc_16(size);
-	}
-	else
-	{ //always use a true hint of static draw when allocating non-client-backed buffers
-		glBufferDataARB(mType, size, 0, GL_STATIC_DRAW_ARB);
-	}
-
-	glBindBufferARB(mType, 0);
-
-#endif
 
 	return ret;
 }
 {
 	llassert(vbo_block_size(size) == size);
 
-#if LL_VBO_POOLING
-
-	U32 i = vbo_block_index(size);
-
-	llassert(mFreeList.size() > i);
-
-	Record rec;
-	rec.mGLName = name;
-	rec.mClientData = buffer;
-	
-	if (buffer == NULL)
-	{
-		glDeleteBuffersARB(1, &rec.mGLName);
-	}
-	else
-	{
-		if (mType == GL_ARRAY_BUFFER_ARB)
-		{
-			sBytesPooled += size;
-		}
-		else
-		{
-			sIndexBytesPooled += size;
-		}
-		mFreeList[i].push_back(rec);
-	}
-#else //no pooling
-	glDeleteBuffersARB(1, &name);
+	deleteBuffer(name);
 	ll_aligned_free_16((U8*) buffer);
 
 	if (mType == GL_ARRAY_BUFFER_ARB)
 	{
 		LLVertexBuffer::sAllocatedIndexBytes -= size;
 	}
-#endif
 }
 
+void LLVBOPool::seedPool()
+{
+	U32 dummy_name = 0;
+
+	if (mFreeList.size() < LL_VBO_POOL_SEED_COUNT)
+	{
+		mFreeList.resize(LL_VBO_POOL_SEED_COUNT);
+	}
+
+	for (U32 i = 0; i < LL_VBO_POOL_SEED_COUNT; i++)
+	{
+		if (mMissCount[i] > mFreeList[i].size())
+		{ 
+			U32 size = i*LL_VBO_BLOCK_SIZE;
+		
+			S32 count = mMissCount[i] - mFreeList[i].size();
+			for (U32 j = 0; j < count; ++j)
+			{
+				allocate(dummy_name, size, true);
+			}
+		}
+	}
+}
+
+
 void LLVBOPool::cleanup()
 {
-	U32 size = 1;
+	U32 size = LL_VBO_BLOCK_SIZE;
 
 	for (U32 i = 0; i < mFreeList.size(); ++i)
 	{
 		{
 			Record& r = l.front();
 
-			glDeleteBuffersARB(1, &r.mGLName);
-
+			deleteBuffer(r.mGLName);
+			
 			if (r.mClientData)
 			{
 				ll_aligned_free_16((void*) r.mClientData);
 			}
 		}
 
-		size *= 2;
+		size += LL_VBO_BLOCK_SIZE;
 	}
+
+	//reset miss counts
+	std::fill(mMissCount.begin(), mMissCount.end(), 0);
 }
 
 
 	GL_LINE_LOOP,
 };
 
+//static
+U32 LLVertexBuffer::getVAOName()
+{
+	U32 ret = 0;
+
+	if (!sAvailableVAOName.empty())
+	{
+		ret = sAvailableVAOName.front();
+		sAvailableVAOName.pop_front();
+	}
+	else
+	{
+#ifdef GL_ARB_vertex_array_object
+		glGenVertexArrays(1, &ret);
+#endif
+	}
+
+	return ret;		
+}
+
+//static
+void LLVertexBuffer::releaseVAOName(U32 name)
+{
+	sAvailableVAOName.push_back(name);
+}
+
+
+//static
+void LLVertexBuffer::seedPools()
+{
+	sStreamVBOPool.seedPool();
+	sDynamicVBOPool.seedPool();
+	sStreamIBOPool.seedPool();
+	sDynamicIBOPool.seedPool();
+}
 
 //static
 void LLVertexBuffer::setupClientArrays(U32 data_mask)
 	if (mGLArray)
 	{
 #if GL_ARB_vertex_array_object
-		glDeleteVertexArrays(1, &mGLArray);
+		releaseVAOName(mGLArray);
 #endif
 	}
 
 
 	llassert(nverts >= 0);
 
-	if (nverts >= 65535)
+	if (nverts > 65536)
 	{
 		llwarns << "Vertex buffer overflow!" << llendl;
-		nverts = 65535;
+		nverts = 65536;
 	}
 
 	U32 needed_size = calcOffsets(mTypeMask, mOffsets, nverts);
 		if (gGLManager.mHasVertexArrayObject && useVBOs() && (LLRender::sGLCoreProfile || sUseVAO))
 		{
 #if GL_ARB_vertex_array_object
-			glGenVertexArrays(1, &mGLArray);
+			mGLArray = getVAOName();
 #endif
 			setupVertexArray();
 		}
 	}
 }
 
+// bind for transform feedback (quick 'n dirty)
+void LLVertexBuffer::bindForFeedback(U32 channel, U32 type, U32 index, U32 count)
+{
+#ifdef GL_TRANSFORM_FEEDBACK_BUFFER
+	U32 offset = mOffsets[type] + sTypeSize[type]*index;
+	U32 size= (sTypeSize[type]*count);
+	glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, channel, mGLBuffer, offset, size);
+#endif
+}
+
 // Set for rendering
 void LLVertexBuffer::setBuffer(U32 data_mask)
 {
 	stop_glerror();
 	volatile U8* base = useVBOs() ? (U8*) mAlignedOffset : mMappedData;
 
-	/*if ((data_mask & mTypeMask) != data_mask)
+	if (gDebugGL && ((data_mask & mTypeMask) != data_mask))
 	{
 		llerrs << "LLVertexBuffer::setupVertexBuffer missing required components for supplied data mask." << llendl;
-	}*/
+	}
 
 	if (LLGLSLShader::sNoFixedFunction)
 	{

File indra/llrender/llvertexbuffer.h

 	static U32 sBytesPooled;
 	static U32 sIndexBytesPooled;
 	
-	LLVBOPool(U32 vboUsage, U32 vboType)
-		: mUsage(vboUsage)
-		, mType(vboType)
-	{}
+	static U32 sCurGLName;
 
+	LLVBOPool(U32 vboUsage, U32 vboType);
+		
 	const U32 mUsage;
 	const U32 mType;
 
 	//size MUST be a power of 2
-	volatile U8* allocate(U32& name, U32 size);
+	volatile U8* allocate(U32& name, U32 size, bool for_seed = false);
 	
 	//size MUST be the size provided to allocate that returned the given name
 	void release(U32 name, volatile U8* buffer, U32 size);
 	
+	//batch allocate buffers to be provided to the application on demand
+	void seedPool();
+
 	//destroy all records in mFreeList
 	void cleanup();
 
+	U32 genBuffer();
+	void deleteBuffer(U32 name);
+
 	class Record
 	{
 	public:
 		volatile U8* mClientData;
 	};
 
+	std::list<U32> mGLNamePool;
+
 	typedef std::list<Record> record_list_t;
 	std::vector<record_list_t> mFreeList;
+	std::vector<U32> mMissCount;
+
 };
 
-class LLGLFence