Please view this problem about multip sceneManager pass

Anything and everything that's related to OGRE or the wider graphics field that doesn't fit into the other forums.
Post Reply
ershu
Gnoblar
Posts: 6
Joined: Mon Apr 21, 2008 9:41 am

Please view this problem about multip sceneManager pass

Post by ershu » Mon Apr 21, 2008 10:13 am

hello, everybody. please help!

Our game project became unstable recently. It's easy to crash.
I spent whole week to find why. And here is my conclusion : please point out for us what's we did wrong .

In our project, we have more than one SceneManager at one time.

OK, the RenderPriorityGroup (in OgreRenderQueueSortingGrouping.cpp) of one of them hold some pass pointer.
Then to free some momery, we remove some Materials from MaterialManager, and the Materials add their children pass pointer
into Pass::graveyard.

Now, one of SceneManager (called 'B' SceneManager) still hold pass* to these which in graveyard. This is bad ! If one of the other
SceneManager::getRenderQueue()::clear() invoke first :

Code: Select all

    void RenderQueue::clear(bool destroyPassMaps)
    {
        // Clear the queues
        RenderQueueGroupMap::iterator i, iend;
        i = mGroups.begin();
        iend = mGroups.end();
        for (; i != iend; ++i)
        {
            i->second->clear(destroyPassMaps);
        }

        // Now trigger the pending pass updates
        Pass::processPendingPassUpdates();

        // NB this leaves the items present (but empty)
        // We're assuming that frame-by-frame, the same groups are likely to 
        //  be used, so no point destroying the vectors and incurring the overhead
        //  that would cause, let them be destroyed in the destructor.
    }
NOTE: Pass::processPendingPassUpdates(); <--- this call cleared the graveyard list !!!!!

Then when our 'B' SceneManager try to invoke addRenderable(...), it's will hit

Code: Select all

    retPair = mGrouped.insert(
                        PassGroupRenderableMap::value_type(
    						pass, new RenderableList() ));
    assert(retPair.second && 
    	"Error inserting new pass entry into PassGroupRenderableMap");
Because of mGrouped contained bad pass pointer !!! And there is no chance for 'B' SceneManager to invoke removePassEntry(...) to remove
invalid pass pointer from mGrouped.

the mGrouped.find(...) sort process will be freak, so we hit this assert! Or in release mode, we will crash later in other place.

In short, my opinion is :
All the SceneManagers shared one Pass::graveyard !!! And they use this list to remove invalid pass pointer from QueuedRenderableCollection,
but after the first one invoke RenderQueue::clear(...), it cleared the graveyard !! Then other SceneManagers after cann't remove
invalid pass pointer, because they lost the chance to remove it.

Is that a bug in OGRE ? How did you avoid this problem I talked before ?

by the way, English is not my mother tongue, but I tried my best to let you understand me. :)
0 x

User avatar
tuan kuranes
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 2653
Joined: Wed Sep 24, 2003 8:07 am
Location: Haute Garonne, France
Contact:

Post by tuan kuranes » Wed Apr 23, 2008 1:02 pm

Then to free some momery, we remove some Materials from MaterialManager, and the Materials add their children pass pointer
into Pass::graveyard.
Why is that ?
Isn't that very hacky way ?
If you need material, why do you free them ?
0 x

ershu
Gnoblar
Posts: 6
Joined: Mon Apr 21, 2008 9:41 am

Post by ershu » Thu Apr 24, 2008 6:30 am

Our project is a real commercial MMORPG. We have developed it for almost 2 years. There're so many materials. If we just unload it, the Manager still hold too many entries.

I have resolved this problem. I'm sure it's a bug of OGRE.
0 x

User avatar
sinbad
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 19261
Joined: Sun Oct 06, 2002 11:19 pm
Location: Guernsey, Channel Islands
Contact:

Post by sinbad » Thu Apr 24, 2008 8:50 pm

I think you've hit this: http://www.ogre3d.org/mantis/view.php?id=130

As mentioned in the bug, it's hard to do generically but possible to get around if you know you have it (which I guess is what you did?). It's been parked for ages waiting for someone to have a good idea of how to solve it cleanly.
0 x

User avatar
gerds
Goblin
Posts: 260
Joined: Mon Sep 01, 2003 3:59 am
Location: London, United Kingdom

Re: Please view this problem about multip sceneManager pass

Post by gerds » Wed Sep 09, 2009 10:54 am

I seem to run in to this problem fairly regularly, this is my current solution and it seems to work well enough.

Add a flag called m_clear_scene_manager_pass_maps which is checked before you call Ogre::Root::RenderOneFrame.

If the flag is true call a method something like ClearSceneManagerPassMaps which iterates through every scene manager, gets its render queue and clears it using clear(true). Then call processPendingPassUpdates.

something like this:

Method to notify your engine that the pass maps need to be cleared:

Code: Select all

        // if a Ogre::Pass hash may have changed, or a material has been removed
        // using Ogre::MaterialManager::getSingleton().remove() the scene manager
        // pass maps must be manually cleared because there is a bug/limitation in ogre.
        // please see ClearSceneManagerPassMaps for more information.
        void _ClearSceneManagerPassMapsRequired()
        {
            m_clear_scene_manager_pass_maps = true;
        }
Your main render cycle code:

Code: Select all

        // clear all scene manager pass maps if required
        if (m_clear_scene_manager_pass_maps)
        {
            ClearSceneManagerPassMaps();
        }

        // tell ogre to render the frame
        m_proot->renderOneFrame();
The ClearSceneManagerPassMaps method:

Code: Select all

// clear all of the scene manager pass maps
void MyEngineWrapper::ClearSceneManagerPassMaps()
{
    m_clear_scene_manager_pass_maps = false;
    if (m_proot)
    {
        Ogre::SceneManagerEnumerator::SceneManagerIterator scenesIter = m_proot->getSceneManagerIterator();
        while (scenesIter.hasMoreElements())
        { 
            Ogre::SceneManager* pScene = scenesIter.getNext(); 
            if (pScene)
            { 
                Ogre::RenderQueue* pQueue = pScene->getRenderQueue(); 
                if (pQueue)
                {
                    pQueue->clear(true);
                }
            }
        }
        Ogre::Pass::processPendingPassUpdates();
    }    
}
So now every time your code causes a change in a pass hash, or removes a material it just has to call

Code: Select all

// ensure the pass maps are cleared because we are changing a pass hash or deleting a material
GetMyEngine()->_ClearSceneManagerPassMapsRequired();
The times when I call this method are:
1) When calling Ogre::MaterialManager::getSingleton().remove
2) When calling Ogre::TextureUnitState::setCurrentFrame
3) When calling Ogre::ResourceGroupManager::getSingleton().unloadResourceGroup
4) When calling Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup
5) When background unloading/destroying of a resource group has completed
6) When calling Ogre::TextureUnitState::setTextureName // technically you only have to do this if you change one of the first two textures (since only two are used for generating the hash - I just call it always)
7) When calling Ogre::Pass::removeTextureUnitState
8 ) When calling Ogre::Pass::removeAllTextureUnitStates

I'm not sure if this is an exhaustive list, but each time someone finds the "Error inserting new pass entry into PassGroupRenderableMap" getting raised we just track down what is causing it and call the "_ClearSceneManagerPassMapsRequired()" method. This seems to fix the problem.

If you can think of any other times we need to clear the pass maps let me know, it might not apply to the way we are using ogre, but i'll certainly look in to it.
0 x

samme
Kobold
Posts: 25
Joined: Thu Jul 08, 2010 12:08 pm

Re: Please view this problem about multip sceneManager pass

Post by samme » Fri Mar 30, 2012 6:23 am

I just encountered this problem yesterday and lucky found this post.

I have made a simpler work-around, which may not be perfect, but it is OGRE only modification.

Since there is multiple scene manager, I assign a "bool mMasterSceneManager" to SceneManager, and make only ONE sceneMgr to be true, all others have to mark FALSE.

Code: Select all

    class _OgreExport SceneManager : public SceneMgtAlloc
    {
                .......
    protected:
 		bool mMasterSceneManager;
                ........
    public:
		void setMaster(bool master=false) { mMasterSceneManager = master;}
                ........
    };
Change the SceneManager's constructor to make master=true

Code: Select all

//-----------------------------------------------------------------------
SceneManager::SceneManager(const String& name) :
mName(name),
......
mMasterSceneManager(true)
{

    // init sky
    for (size_t i = 0; i < 5; ++i)
Next, change RenderQueue::clear(bool destroyPassMaps) to (you have to change the header file):

Code: Select all

    void RenderQueue::clear(bool destroyPassMaps, bool processPendingPassUpdates)
    {
        // Clear the queues
        RenderQueueGroupMap::iterator i, iend;
        i = mGroups.begin();
        iend = mGroups.end();
        for (; i != iend; ++i)
        {
            i->second->clear(destroyPassMaps);
        }

		if (processPendingPassUpdates)
	        // Now trigger the pending pass updates
		    Pass::processPendingPassUpdates();

        // NB this leaves the items present (but empty)
        // We're assuming that frame-by-frame, the same groups are likely to 
        //  be used, so no point destroying the vectors and incurring the overhead
        //  that would cause, let them be destroyed in the destructor.
    }
In OgreSceneManager.cpp

Code: Select all

//-----------------------------------------------------------------------
void SceneManager::prepareRenderQueue(void)
{
	RenderQueue* q = getRenderQueue();
	// Clear the render queue
	q->clear(Root::getSingleton().getRemoveRenderQueueStructuresOnClear(), mMasterSceneManager);
In OgreOctreeSceneManager.cpp (apply to other inherit scene manager class)

Code: Select all

void OctreeSceneManager::_findVisibleObjects(Camera * cam, 
	VisibleObjectsBoundsInfo* visibleBounds, bool onlyShadowCasters )
{

    getRenderQueue()->clear(false,mMasterSceneManager);
So far it work ok. Hope someone found it's useful. :D
0 x

User avatar
madmarx
OGRE Expert User
OGRE Expert User
Posts: 1669
Joined: Mon Jan 21, 2008 10:26 pm

Re: Please view this problem about multip sceneManager pass

Post by madmarx » Fri Mar 30, 2012 9:52 pm

Great post!
0 x
Tutorials + Ogre searchable API + more for Ogre1.7 : http://sourceforge.net/projects/so3dtools/
Corresponding thread : http://www.ogre3d.org/forums/viewtopic. ... 93&start=0

Post Reply