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 {