[sldev] Re: Reviewing/Fixing the Particle System
Nicholaz Beresford
nicholaz at blueflash.cc
Mon May 28 16:54:00 PDT 2007
I'm attaching a new version for the particle system (not much
different than the last patch, but including an idea from Argent
Stonecutter on how to apply a better filter).
I visited Apollo today and looked around and also saw a wild
excess of particle sources. Tried to increase number of particles
and found (as reported ealier) that the number is capped at 4096
in viewerpartsim.cpp
I also saw that shouldAddPart() (I also mentioned that before)
does some randomizing at a fill of 75% or higher and does the
cap at 4096 too. It is called for other particle sources like spiral,
beam and chat.
Which brought me to the idea that my experimental filter
(currently throttlePartSource() in llviewerpartsource) could just
replace this function, since they're both doing the same thing
more or less anyway.
Thoughts?
Nick
Second Life from the inside out:
http://nicholaz-beresford.blogspot.com/
-------------- next part --------------
--- linden-orig/indra/newview/llviewerpartsim.h 2007-05-26 01:48:21.531250000 +0200
+++ linden/indra/newview/llviewerpartsim.h 2007-05-26 01:48:23.765625000 +0200
@@ -138,6 +138,8 @@
void cleanupRegion(LLViewerRegion *regionp);
BOOL shouldAddPart(); // Just decides whether this particle should be added or not (for particle count capping)
+ F32 getPartFillRatio() { return (F32)sParticleCount / (F32)sMaxParticleCount; }
+
void addPart(LLViewerPart* part);
void cleanMutedParticles(const LLUUID& task_id);
--- linden-orig/indra/newview/llviewerpartsim.cpp 2007-05-23 11:56:00.000000000 +0200
+++ linden/indra/newview/llviewerpartsim.cpp 2007-05-28 15:21:50.406250000 +0200
@@ -521,6 +521,7 @@
llwarns << "LLViewerPartSim::put - Particle didn't go into its box!" << llendl;
llinfos << groupp->getCenterAgent() << llendl;
llinfos << part->mPosAgent << llendl;
+ delete groupp; // PATCH: VWR-794 potential memory leak fixed (loss of groupp) [Nicholaz Beresford]
return NULL;
}
return groupp;
@@ -668,6 +669,23 @@
}
}
}
+
+
+ // DEBUG [Nicholas Beresford]
+ //
+ {
+ static U64 clock_last = get_cpu_clock_count();
+ static U64 clock_freq = LLFastTimer::countsPerSecond();
+ U64 now= get_cpu_clock_count();
+
+ if (now-clock_last > clock_freq) { // particle count every second
+ llinfos << sParticleCount << " particles " << " (" << (S32)mViewerPartSources.size() << " sources)" << llendl;
+ clock_last= now;
+ }
+ }
+ //
+ // ~DEBUG
+
//llinfos << "Particles: " << sParticleCount << llendl;
}
@@ -693,7 +711,17 @@
if ((*iter)->getRegion() == regionp)
{
+ // PATCH: memory leak [Nicholaz Beresford]
+ //
+ // VWR-794
+
+ // keep sequence: remember the pointer, delete from list, delete object
+ LLViewerPartGroup *groupp= (*iter);
i = mViewerPartGroups.erase(iter);
+ delete groupp; // need to delete object associated with pointer not just
+ // erase pointer from pointer-list
+ //
+ // ~PATCH
}
}
}
--- linden-orig/indra/newview/llviewerpartsource.h 2007-05-23 11:56:00.000000000 +0200
+++ linden/indra/newview/llviewerpartsource.h 2007-05-26 02:05:04.515625000 +0200
@@ -110,6 +110,7 @@
static LLViewerPartSourceScript *unpackPSS(LLViewerObject *source_objp, LLViewerPartSourceScript *pssp, const S32 block_num);
static LLViewerPartSourceScript *unpackPSS(LLViewerObject *source_objp, LLViewerPartSourceScript *pssp, LLDataPacker &dp);
+ F32 throttlePartSource(void);
LLViewerImage *getImage() const { return mImagep; }
void setImage(LLViewerImage *imagep);
LLPartSysData mPartSysData;
--- linden-orig/indra/newview/llviewerpartsource.cpp 2007-05-23 11:56:00.000000000 +0200
+++ linden/indra/newview/llviewerpartsource.cpp 2007-05-28 15:39:16.671875000 +0200
@@ -37,6 +37,7 @@
#include "llviewerobject.h"
#include "llviewerobjectlist.h"
#include "llvoavatar.h"
+#include "llviewercamera.h" // PATCH: used further down below [Nicholaz]
#include "llworld.h"
#include "pipeline.h"
@@ -80,8 +81,7 @@
mImagep = gImageList.getImage(id);
mImagep->bind();
mImagep->setClamp(TRUE, TRUE);
-}
-
+}
void LLViewerPartSourceScript::setDead()
{
@@ -96,7 +96,7 @@
LLMemType mt(LLMemType::MTYPE_PARTICLES);
F32 old_update_time = mLastUpdateTime;
mLastUpdateTime += dt;
-
+
F32 dt_update = mLastUpdateTime - mLastPartTime;
// Update this for objects which have the follow flag set...
@@ -148,6 +148,13 @@
}
+ // EXPERIMENTAL: throttle particle generation [Nicholaz Beresford]
+ //
+ F32 quota = throttlePartSource();
+ //
+ // ~EXPERIMENTAL
+
+
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_PARTICLES))
{
if (mSourceObjectp.notNull())
@@ -196,13 +203,40 @@
S32 i;
for (i = 0; i < mPartSysData.mBurstPartCount; i++)
{
+
+ // EXPERIMENTAL: throttle particle generation [Nicholaz Beresford]
+ //
+ // (NOTE: just multiplying the quota with the loop's mPartSysData.mBurstPartCount
+ // won't work because often these values are fairly small (just a count of 1-3)
+ // and don't lend themselves to multiplying with 20% or so.
+ //
+ // Therefore a statistical method works better: whenever a particle is needed,
+ // it is generated when a random number meets the percentage threshold (which
+ // over time ensures generation of the desired quota of particles)
+
+
+ /* This is a simplified Linden randomized filter which should
+ not be necessary anmore with the patched stuff below
if (!gWorldPointer->mPartSim.shouldAddPart())
{
// Particle simulation says we have too many particles, skip all this
continue;
}
+ */
+
+ if (ll_frand() > quota) { // let quota percentage of the particles pass
+ continue; // skip this particle, try our luck with the next iteration
+ }
+
+ // hooray ... a new particle
LLPointer<LLViewerPart> part = new LLViewerPart();
+ if (part.isNull()) {
+ break; // oops, out of memory
+ }
+ //
+ // ~EXPERIMENTAL
+
part->init(this, mImagep, NULL);
part->mFlags = mPartSysData.mPartData.mFlags;
@@ -309,6 +343,111 @@
}
}
+
+// EXPERIMENTAL [Nicholas Beresford]
+//
+// throttle particle source
+F32 LLViewerPartSourceScript::throttlePartSource()
+{
+ // quota of generated particles to be actually generated [0 .. 1.f / 0% ... 100%]
+ // (calculating a lower pass quota will limit the emission of the particle source)
+ F32 pass_quota = 1.f,
+ usage = gWorldPointer->mPartSim.getPartFillRatio(); // percentage of max-particles in the system now
+
+
+ // just safety ... 0% quota for non drawable objects
+ if (mSourceObjectp->mDrawable.isNull() || usage > 0.99f)
+ return 0;
+
+
+ // Generate less of the intended particles the further away the emitter is,
+ // Current particle usage will define what is considered "far"
+ // (something between draw_dist*4 and draw_dist/4 in this example)
+ F32 draw_dist = gAgent.mDrawDistance,
+ dist_cam = 1, // distance from camera
+ view_limit_low = draw_dist / 4, // view_limit with total particle numbers near 100% of max-particles
+ view_limit_high = draw_dist * 4, // view_limit with no or very little particles
+ view_limit;
+
+ LLPartSysData &psd = mPartSysData;
+ LLPartData &pd = psd.mPartData;
+
+ psd; // keep the friggin' VS2003 compiler happy
+ pd; // keep the friggin' VS2003 compiler happy
+
+
+ // vary view limit depending on usage. view_limit determines the quota an emitter
+ // may generate: 100% generation a 0-meters distance, 0% generation at a distance of
+ // view_limit-meters (or more) and proportional quotas in between (based on distance)
+ // (with a view_limit of draw_dist*4 the most distant visible (at draw_distance) source could
+ // generate at a quota of 75%)
+ view_limit = view_limit_low + (1.f - usage) * (view_limit_high - view_limit_low);
+
+
+ // get distance from camera
+ // (we could also do: LLVector3 dist= (mPosAgent - gCamera->getOrigin()).magVec())
+ LLViewerObject* srcobjp= mSourceObjectp;
+ if (srcobjp && srcobjp->mDrawable.notNull()) {
+ dist_cam = srcobjp->mDrawable->mDistanceWRTCamera;
+ }
+
+
+ // generate quota of allowance (from 110% to zero)
+ //
+ // (normally this would start from 1.f (100%) but this gives
+ // nearer particles extra leeway, i.e. make the filter not cut
+ // in from the first meter)
+
+ // dissed in favor of Argent Stonecutter's version below.
+ pass_quota = 1.1f - dist_cam / view_limit;
+
+
+
+ // here we could further tweak the allowance based on
+ // emission, size of particles, cloud size, camera direction
+ // (direction may be a real winner, anyone in for some vector geometry??)
+
+ // S32 parts_per_second = (S32)((1.f/psd.mBurstRate)*(S32)psd.mBurstPartCount); // particle generation rate
+ // F32 active_parts_max = parts_per_second * pd.mMaxAge; // max. theoretical number of parts after swinging in
+
+ // yada yada yada
+
+
+ // Agent Stonecutter's filter
+ /*
+
+ F32 scaleSq, distSq;
+
+ distSq = (mPosAgent - gCamera->getOrigin()).magVec();
+ distSq /= view_limit; // scale to view limit
+ distSq *= distSq; // So we don't have to make with square roots
+
+
+ if (pd.mFlags & LLPartData::LL_PART_INTERP_SCALE_MASK)
+ {
+ scaleSq = (pd.mEndScale[0] + pd.mStartScale[0]) * (pd.mEndScale[1] + pd.mStartScale[1]) / 4.0f; // avg(x) = (x1+x2)/2, factored out
+ }
+ else
+ {
+ scaleSq = pd.mStartScale[0] * pd.mStartScale[1];
+ }
+ scaleSq /= 16.0f; // biggest particle is 4x4, so this scales "particle scale factor" to a range 1...0
+
+ pass_quota = scaleSq / distSq;
+ */
+
+
+ // return 0-100% pass through rate for the filter
+ pass_quota = llclamp(pass_quota, 0.f, 1.f);
+
+ return pass_quota;
+}
+//
+// ~EXPERIMENTAL
+
+
+
+
// static
LLViewerPartSourceScript *LLViewerPartSourceScript::unpackPSS(LLViewerObject *source_objp, LLViewerPartSourceScript *pssp, const S32 block_num)
{
@@ -322,6 +461,7 @@
LLViewerPartSourceScript *new_pssp = new LLViewerPartSourceScript(source_objp);
if (!new_pssp->mPartSysData.unpackBlock(block_num))
{
+ delete new_pssp; // PATCH: VWR-865 potential memory leak fixed (loss of new_pssp) [Nicholaz Beresford]
return NULL;
}
if (new_pssp->mPartSysData.mTargetUUID.notNull())
@@ -360,6 +500,7 @@
LLViewerPartSourceScript *new_pssp = new LLViewerPartSourceScript(source_objp);
if (!new_pssp->mPartSysData.unpack(dp))
{
+ delete new_pssp; // PATCH: VWR-865 potential memory leak fixed (loss of new_pssp) [Nicholaz Beresford]
return NULL;
}
if (new_pssp->mPartSysData.mTargetUUID.notNull())
@@ -607,6 +748,7 @@
if (dt_update > RATE)
{
mLastPartTime = mLastUpdateTime;
+
if (!gWorldPointer->mPartSim.shouldAddPart())
{
// Particle simulation says we have too many particles, skip all this
More information about the SLDev
mailing list