ParticleSystem2 is never submitted when its render queue id exceeds the highest slot occupied by a regular MovableObject

Discussion area about developing with Ogre-Next (2.1, 2.2 and beyond)


Post Reply
Lax
Orc Shaman
Posts: 721
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 83
Contact:

ParticleSystem2 is never submitted when its render queue id exceeds the highest slot occupied by a regular MovableObject

Post by Lax »

Hi @dark_sylinc,

I hit a case where a ParticleSystem2 simulates perfectly — correct world position, valid AABB, visibility flags matching the camera, getNumSimdActiveParticles() counting up as expected — but no draw call is ever submitted for it. A RenderDoc capture shows every other draw in the scene pass, and simply nothing for the particles.

The cause is an implicit coupling between ParticleSystemManager2 and ObjectMemoryManager.

In SceneManager::cullFrustum(), the render queue loop is bounded by the object memory manager:

Code: Select all

const size_t numRenderQueues = memoryManager->getNumRenderQueues();
size_t firstRq = std::min<size_t>( request.firstRq, numRenderQueues );
size_t lastRq  = std::min<size_t>( request.lastRq,  numRenderQueues );

and the particle submission happens inside that loop:

Code: Select all

if( mRenderQueue->getRenderQueueMode( currRqId ) == RenderQueue::PARTICLE_SYSTEM &&
    request.addToRenderQueue )
{
    mParticleSystemManager2->_addToRenderQueue( threadIdx, mNumWorkerThreads,
                                                mRenderQueue, currRqId, visibilityMask,
                                                !request.casterPass );
}

ObjectMemoryManager::getNumRenderQueues() returns the highest slot actually occupied by a real MovableObject, plus one:

cpp
while( itor != endt )
{
    if( itor->getUsedMemory() )
        retVal = static_cast<size_t>( itor - begin );
    ++itor;
}
return retVal + 1;

So if a ParticleSystemDef sits on, say, render queue 214, but no ordinary object occupies a slot at or above 214, the loop stops earlier and _addToRenderQueue is never called for 214. The particle system is configured correctly in every respect and still renders nothing.

This is easy to miss because it depends on unrelated scene content. In my case the editor build rendered the particles fine while the standalone game did not — same scene file, same code path. The difference was that the editor has gizmo geometry on render queue 220, which stretched the iterated range far enough to cover 214 by pure coincidence. Remove the selection, or run the game where no gizmos exist, and the particles vanish.

That did cost nerves. I searched for 3 days chasing this issue.

It also explains why kParticleSystemDefaultRenderQueueId = 15 works reliably in the samples: it is low enough to always fall inside the range occupied by ordinary objects.

My current workaround is to attach an invisible dummy Item to a high render queue purely to keep getNumRenderQueues() large enough. That works, but it means paying for the extra empty queue iterations every frame, and it is not something a user would ever guess.

Would it make sense to drive the particle submission from request.firstRq / request.lastRq — the range the compositor pass actually asked for — instead of from the object memory manager's occupancy? The particle definitions live in ParticleSystemManager2, not in ObjectMemoryManager, so bounding them by the latter seems accidental rather than intended.

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5586
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1413
Contact:

Re: ParticleSystem2 is never submitted when its render queue id exceeds the highest slot occupied by a regular MovableOb

Post by dark_sylinc »

What an annoying little bug! I can it would drive you crazy looking for it!

Does it get fixed if the code block is changed with something like this? (remember to turn off your workaround)

Code: Select all

        const size_t numParticleRqs = std::max( mParticleSysDefMemoryManager.getNumRenderQueues(),
                                                mParticleSysMemoryManager.getNumRenderQueues() );

        while( it != en )
        {
            ObjectMemoryManager *memoryManager = *it;
            const size_t numRenderQueues =
                std::max( memoryManager->getNumRenderQueues(), numParticleRqs );

            size_t firstRq = std::min<size_t>( request.firstRq, numRenderQueues );
            size_t lastRq = std::min<size_t>( request.lastRq, numRenderQueues );

Would it make sense to drive the particle submission from request.firstRq / request.lastRq — the range the compositor pass actually asked for — instead of from the object memory manager's occupancy? The particle definitions live in ParticleSystemManager2, not in ObjectMemoryManager, so bounding them by the latter seems accidental rather than intended.

That is also a possible solution. The goal was to restrict the loop to what is actually being used, it's a cheap evaluation to save some CPU cycles.

Lax
Orc Shaman
Posts: 721
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 83
Contact:

Re: ParticleSystem2 is never submitted when its render queue id exceeds the highest slot occupied by a regular MovableOb

Post by Lax »

Hi,

yes. That would fix it!

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

Post Reply