diff --git a/indra/newview/fsvopartgroup.cpp b/indra/newview/fsvopartgroup.cpp
new file mode 100644
index 0000000000..b4b61e54d3
--- /dev/null
+++ b/indra/newview/fsvopartgroup.cpp
@@ -0,0 +1,66 @@
+U32 sFreeIndex[LL_MAX_PARTICLE_COUNT/32] = {0};
+U32 sIndexGeneration = 1;
+U32 sTotalParticles = 0;
+
+U32 bitMasks[ 32 ] = { 0xFFFFFFFF, 0x80000000, 0xC0000000, 0xE0000000,
+ 0xF0000000, 0xF8000000, 0xFC000000, 0xFE000000,
+ 0xFF000000, 0xFF800000, 0xFFC00000, 0xFFE00000,
+ 0xFFF00000, 0xFFF80000, 0xFFFC0000, 0xFFFE0000,
+ 0xFFFF0000, 0xFFFF8000, 0xFFFFC000, 0xFFFFE000,
+ 0xFFFFF000, 0xFFFFF800, 0xFFFFFC00, 0xFFFFFE00,
+ 0xFFFFFF00, 0xFFFFFF80, 0xFFFFFFC0, 0xFFFFFFE0,
+ 0xFFFFFFF0, 0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE };
+
+bool findAvailableVBSlots( S32 &idxStart, S32 &idxEnd, U32 amount )
+{
+ idxStart = idxEnd = -1;
+
+ if( amount + sTotalParticles > LL_MAX_PARTICLE_COUNT )
+ amount = LL_MAX_PARTICLE_COUNT - sTotalParticles;
+
+ U32 u32Count = amount/32;
+ U32 bitsLeft = amount - (u32Count*32);
+ if( bitsLeft )
+ ++u32Count;
+ U32 maskLast = bitMasks[ bitsLeft ];
+
+ int i = 0;
+ int maxI = LL_MAX_PARTICLE_COUNT/32-u32Count;
+ do
+ {
+ while( sFreeIndex[i] != 0xFFFFFFFF && i <= maxI )
+ ++i;
+
+ if( i > maxI )
+ continue;
+
+ int j = i;
+ while( sFreeIndex[j] == 0xFFFFFFFF && j <= LL_MAX_PARTICLE_COUNT/32 && (j-i) != u32Count )
+ ++j;
+
+ if( j > LL_MAX_PARTICLE_COUNT/32 || (i-j) != u32Count || (sFreeIndex[j-1] & maskLast) != maskLast )
+ {
+ ++i;
+ continue;
+ }
+
+ int k = 0;
+ idxStart = i*32;
+ idxEnd = i + amount;
+ while( k != amount )
+ {
+ for( int l = 0; k != amount && l < 32; ++l )
+ {
+ U32 mask = 1 << l;
+ sFreeIndex[ i ] &= ~mask;
+ ++k;
+ }
+ ++i;
+ }
+
+ sTotalParticles += amount;
+ return true;
+
+ } while( i <= maxI);
+ return false;
+}
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index 9c0205765b..a7378b093c 100755
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -166,6 +166,8 @@ void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp)
mBoundingSphereRadius = 0.0f ;
mHasMedia = FALSE ;
+
+ mParticleGeneration = 0; // Default = no particle
}
void LLFace::destroy()
@@ -183,9 +185,14 @@ void LLFace::destroy()
}
}
- if (isState(LLFace::PARTICLE))
+ // Make sure we released any allocated VB index if this was a particle
+ // if (isState(LLFace::PARTICLE))
+ if (isState(LLFace::PARTICLE) || mParticleGeneration )
+ //
{
- LLVOPartGroup::freeVBSlot(getGeomIndex()/4);
+ LLVOPartGroup::freeVBSlot(getGeomIndex()/4,mParticleGeneration);
+ mParticleGeneration = 0;
+
clearState(LLFace::PARTICLE);
}
@@ -414,8 +421,16 @@ void LLFace::setSize(S32 num_vertices, S32 num_indices, bool align)
llassert(verify());
}
-void LLFace::setGeomIndex(U16 idx)
+// Pass another flag to mark this index as from LLVOPartGroup, in that case it needs to be freed with LLVOPartGroup::LLVOPartGroup
+// void LLFace::setGeomIndex(U16 idx)
+void LLFace::setGeomIndex(U16 idx, U32 aParticleGeneration )
+//
{
+ if( mParticleGeneration && mGeomIndex != idx )
+ LLVOPartGroup::freeVBSlot(getGeomIndex()/4,mParticleGeneration);
+
+ mParticleGeneration = aParticleGeneration;
+
if (mGeomIndex != idx)
{
mGeomIndex = idx;
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
index d3a561facc..b780322823 100755
--- a/indra/newview/llface.h
+++ b/indra/newview/llface.h
@@ -207,7 +207,11 @@ public:
BOOL verify(const U32* indices_array = NULL) const;
void printDebugInfo() const;
- void setGeomIndex(U16 idx);
+ // Pass another flag to mark this index as from LLVOPartGroup, in that case it needs to be freed with LLVOPartGroup::LLVOPartGroup
+ // void setGeomIndex(U16 idx);
+ void setGeomIndex(U16 idx, U32 aParticleGeneration = 0);
+ //
+
void setIndicesIndex(S32 idx);
void setDrawInfo(LLDrawInfo* draw_info);
@@ -359,6 +363,9 @@ public:
lhs->getTexture() < rhs->getTexture();
}
};
+
+private:
+ U32 mParticleGeneration;
};
#endif // LL_LLFACE_H
diff --git a/indra/newview/llviewerpartsim.cpp b/indra/newview/llviewerpartsim.cpp
index f7eb7df46c..73f85c161d 100755
--- a/indra/newview/llviewerpartsim.cpp
+++ b/indra/newview/llviewerpartsim.cpp
@@ -284,16 +284,36 @@ void LLViewerPartGroup::updateParticles(const F32 lastdt)
LLViewerCamera* camera = LLViewerCamera::getInstance();
LLViewerRegion *regionp = getRegion();
S32 end = (S32) mParticles.size();
- for (S32 i = 0 ; i < (S32)mParticles.size();)
+
+ // Instead of walking the current particles back to front, we first make a copy, then sort by adress and refill mParticles as needed.
+ mParticlesTemp.swap( mParticles );
+ mParticles.erase( mParticles.begin(), mParticles.end() );
+ std::sort( mParticlesTemp.begin(), mParticlesTemp.end() );
+
+ // for (S32 i = 0 ; i < (S32)mParticles.size(); )
+ for (S32 i = 0 ; i < (S32)mParticlesTemp.size(); ++i )
+ //
{
- LLVector3 a(0.f, 0.f, 0.f);
- LLViewerPart* part = mParticles[i] ;
+ // LLVector3 a(0.f, 0.f, 0.f); // Unused
+ LLViewerPart* part = mParticlesTemp[i] ;
dt = lastdt + mSkippedTime - part->mSkipOffset;
part->mSkipOffset = 0.f;
// Update current time
const F32 cur_time = part->mLastUpdateTime + dt;
+
+ // Bail out as soon as possible to avoid all the complicated work down.
+ if( cur_time > part->mMaxAge || LLViewerPart::LL_PART_DEAD_MASK == part->mFlags )
+ {
+ if (part->mVPCallback)
+ (*part->mVPCallback)(*part, dt);
+
+ delete part ;
+ continue;
+ }
+ //
+
const F32 frac = cur_time / part->mMaxAge;
// "Drift" the object based on the source object
@@ -397,26 +417,34 @@ void LLViewerPartGroup::updateParticles(const F32 lastdt)
part->mLastUpdateTime = cur_time;
+ // We did that above
// Kill dead particles (either flagged dead, or too old)
- if ((part->mLastUpdateTime > part->mMaxAge) || (LLViewerPart::LL_PART_DEAD_MASK == part->mFlags))
- {
- mParticles[i] = mParticles.back() ;
- mParticles.pop_back() ;
- delete part ;
- }
- else
+ // if ((part->mLastUpdateTime > part->mMaxAge) || (LLViewerPart::LL_PART_DEAD_MASK == part->mFlags))
+ // {
+ // mParticles[i] = mParticles.back() ;
+ // mParticles.pop_back() ;
+ // delete part ;
+ // }
+ // else
+ //
{
F32 desired_size = calc_desired_size(camera, part->mPosAgent, part->mScale);
if (!posInGroup(part->mPosAgent, desired_size))
{
// Transfer particles between groups
LLViewerPartSim::getInstance()->put(part) ;
- mParticles[i] = mParticles.back() ;
- mParticles.pop_back() ;
+
+ // No need for any transfer, just to not add to mParticles.
+ // mParticles[i] = mParticles.back() ;
+ // mParticles.pop_back() ;
+ //
}
else
{
- i++ ;
+ // Keep current particle in this group.
+ // i++ ;
+ mParticles.push_back( part );
+ //
}
}
}
@@ -892,3 +920,76 @@ void LLViewerPartSim::clearParticlesByOwnerID(const LLUUID& task_id)
}
}
+// Object pool for LLViewerPart
+U8 *sParts;
+U8 *sPartsEnd;
+U32 sFree[ LL_MAX_PARTICLE_COUNT/32 ];
+U32 sPartSize;
+
+S32 findFreeIndex()
+{
+ for( int i = 0; i < LL_MAX_PARTICLE_COUNT/32; ++i )
+ {
+ if( sFree[i] )
+ {
+ U32 val = sFree[i];
+ U32 mask = 1;
+ int j(0);
+ for( j = 0; j < 32; ++j )
+ {
+ if( mask & val )
+ {
+ mask = ~mask;
+ break;
+ }
+
+ mask <<= 1;
+ }
+
+ sFree[ i ] = val & mask;
+ return i*32+j;
+ }
+ }
+ return -1;
+}
+
+void* LLViewerPart::operator new(size_t size)
+{
+ if( !sParts )
+ {
+ sPartSize = sizeof( LLViewerPart );
+ sPartSize += 0xF;
+ sPartSize &= ~0xF;
+ sParts = reinterpret_cast( ll_aligned_malloc<16>( sPartSize * LL_MAX_PARTICLE_COUNT ) );
+ for( int i = 0; i < LL_MAX_PARTICLE_COUNT/32; ++i )
+ sFree[ i ] = 0xFFFFFFFF;
+
+ sPartsEnd = sParts + sPartSize * LL_MAX_PARTICLE_COUNT;
+ }
+
+ S32 i = findFreeIndex();
+ if( i < 0 )
+ return new char[ size ];
+
+ return reinterpret_cast< void* >( sParts + sPartSize*i );
+}
+
+void LLViewerPart::operator delete(void* ptr)
+{
+ if( ptr < sParts || ptr >= sPartsEnd )
+ {
+ delete [] (char*)ptr;
+ return;
+ }
+
+ U32 diff = static_cast( reinterpret_cast(ptr) - sParts );
+ diff /= sPartSize;
+
+ U32 i = (diff & ~0x0000001F) / 32;
+ U32 j = diff & 0x0000001F;
+
+ U32 mask = 1 << j;
+
+ sFree[ i ] |= mask;
+}
+//
diff --git a/indra/newview/llviewerpartsim.h b/indra/newview/llviewerpartsim.h
index 40e8e1d45d..501fe9b24b 100755
--- a/indra/newview/llviewerpartsim.h
+++ b/indra/newview/llviewerpartsim.h
@@ -81,6 +81,10 @@ public:
static U32 sNextPartID;
+ // Object pool for LLViewerPart
+ void* operator new(size_t size);
+ void operator delete(void* ptr);
+ //
};
@@ -108,6 +112,7 @@ public:
typedef std::vector part_list_t;
part_list_t mParticles;
+ part_list_t mParticlesTemp; // Temporary list for iteration in updateParticles
const LLVector3 &getCenterAgent() const { return mCenterAgent; }
S32 getCount() const { return (S32) mParticles.size(); }
diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp
index f90d827827..4107130a67 100755
--- a/indra/newview/llvopartgroup.cpp
+++ b/indra/newview/llvopartgroup.cpp
@@ -51,6 +51,8 @@ extern U64MicrosecondsImplicit gFrameTime;
LLPointer LLVOPartGroup::sVB = NULL;
S32 LLVOPartGroup::sVBSlotCursor = 0;
+#include "fsvopartgroup.cpp"
+
void LLVOPartGroup::initClass()
{
@@ -59,6 +61,11 @@ void LLVOPartGroup::initClass()
//static
void LLVOPartGroup::restoreGL()
{
+ sIndexGeneration += 2;
+
+ for( int i = 0; i < LL_MAX_PARTICLE_COUNT/32; ++i )
+ sFreeIndex[i] = 0xFFFFFFFF;
+
//TODO: optimize out binormal mask here. Specular and normal coords as well.
sVB = new LLVertexBuffer(VERTEX_DATA_MASK | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2, GL_STREAM_DRAW_ARB);
@@ -118,12 +125,39 @@ void LLVOPartGroup::destroyGL()
//static
S32 LLVOPartGroup::findAvailableVBSlot()
{
- if (sVBSlotCursor >= LL_MAX_PARTICLE_COUNT)
- { //no more available slots
- return -1;
+ for( int i = 0; i < LL_MAX_PARTICLE_COUNT/32; ++i )
+ {
+ if( sFreeIndex[i] != 0 )
+ {
+ U32 val = sFreeIndex[i];
+ U32 mask = 1;
+ int j(0);
+ for( j = 0; j < 32; ++j )
+ {
+ if( mask & val )
+ {
+ mask = ~mask;
+ break;
+ }
+
+ mask <<= 1;
+ }
+
+ sFreeIndex[ i ] = val & mask;
+ ++sTotalParticles;
+
+ return i*32+j;
+ }
}
- return sVBSlotCursor++;
+ return -1;
+
+// if (sVBSlotCursor >= LL_MAX_PARTICLE_COUNT)
+// { //no more available slots
+// return -1;
+// }
+//
+// return sVBSlotCursor++;
}
bool ll_is_part_idx_allocated(S32 idx, S32* start, S32* end)
@@ -142,7 +176,8 @@ bool ll_is_part_idx_allocated(S32 idx, S32* start, S32* end)
}
//static
-void LLVOPartGroup::freeVBSlot(S32 idx)
+// void LLVOPartGroup::freeVBSlot(S32 idx)
+void LLVOPartGroup::freeVBSlot(S32 idx, U32 generation )
{
/*llassert(idx < LL_MAX_PARTICLE_COUNT && idx >= 0);
//llassert(sVBSlotCursor > sVBSlotFree);
@@ -153,6 +188,21 @@ void LLVOPartGroup::freeVBSlot(S32 idx)
sVBSlotCursor--;
*sVBSlotCursor = idx;
}*/
+
+ if( idx < 0 || idx >= LL_MAX_PARTICLE_COUNT )
+ return;
+
+ --sTotalParticles;
+
+ if( sIndexGeneration != generation )
+ return;
+
+ U32 i = (idx & ~0x0000001F) / 32;
+ U32 j = idx & 0x0000001F;
+
+ U32 mask = 1 << j;
+
+ sFreeIndex[ i ] |= mask;
}
LLVOPartGroup::LLVOPartGroup(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
@@ -859,6 +909,14 @@ void LLParticlePartition::getGeometry(LLSpatialGroup* group)
LLSpatialGroup::drawmap_elem_t& draw_vec = group->mDrawMap[mRenderPass];
+ // Free all vb slots. Then try to allocate an adjacent block of slots for all faces
+ for( std::vector::iterator itr = mFaceList.begin(); itr != mFaceList.end(); ++itr )
+ (*itr)->setGeomIndex( 0 );
+
+ S32 idxStart, idxEnd;
+ bool bValidRange = findAvailableVBSlots( idxStart, idxEnd, mFaceList.size() );
+ //
+
for (std::vector::iterator i = mFaceList.begin(); i != mFaceList.end(); ++i)
{
LLFace* facep = *i;
@@ -866,10 +924,26 @@ void LLParticlePartition::getGeometry(LLSpatialGroup* group)
//if (!facep->isState(LLFace::PARTICLE))
{ //set the indices of this face
- S32 idx = LLVOPartGroup::findAvailableVBSlot();
+
+ // Can we use a index from our preallocated list?
+ // S32 idx = LLVOPartGroup::findAvailableVBSlot();
+ S32 idx(-1);
+ if( bValidRange )
+ {
+ if( idxStart < idxEnd )
+ idx = idxStart++;
+ }
+ else
+ idx = LLVOPartGroup::findAvailableVBSlot();
+ //
+
if (idx >= 0)
{
- facep->setGeomIndex(idx*4);
+ // Face needs to release the index when it gets destroyed.
+ // facep->setGeomIndex(idx*4);
+ facep->setGeomIndex(idx*4, sIndexGeneration);
+ //
+
facep->setIndicesIndex(idx*6);
facep->setVertexBuffer(LLVOPartGroup::sVB);
facep->setPoolType(LLDrawPool::POOL_ALPHA);
diff --git a/indra/newview/llvopartgroup.h b/indra/newview/llvopartgroup.h
index 2ef8b1c848..e245a2bc16 100755
--- a/indra/newview/llvopartgroup.h
+++ b/indra/newview/llvopartgroup.h
@@ -48,7 +48,9 @@ public:
static void restoreGL();
static void destroyGL();
static S32 findAvailableVBSlot();
- static void freeVBSlot(S32 idx);
+
+ // static void freeVBSlot(S32 idx);
+ static void freeVBSlot(S32 idx, U32 generation);
enum
{