How to release the memory created by SphereSceneNode?

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
KevinLuo
Posts: 23
Joined: Sat Apr 15, 2017 6:08 am

How to release the memory created by SphereSceneNode?

Post by KevinLuo »

Hi,

I have some trouble releasing the memory of the SphereSceneNode.

I create a class "CSphereObeject" derived from ISceneNode. I add a sphere scene node for each instance of CSphereObject,
by calling "meshSceneNode = SceneManager->addSphereSceneNode(5,16,this)." Notice that the instance of CSphereObject
is the parent node of the meshSceneNode.

When I'm done with the CSphereObject, I try to call the remove() method of CSphereObject to release the memory of it.
And I detected the memory leak occurred at the stack of the sphere scene node, according to VisualStudio Debug tools.
The stack analyze tool says, the memory created by CGeometryCreator::createSphereMesh() isn't released after remove().
And I checked the memory viewer, the memory of IMeshSceneNode had been released but the memory of IMesh part remained.

And I tried to call meshSceneNode->remove() at the deconstructing function of CSphereObject. It didn't work. Then I tried
to call SceneManager->getMeshCache()->removeMesh(meshSceneNode->getMesh()), it didn't work. I really don't know how
to release the memory of the sphere mesh scene node. Please show me the way!

Regards,
Kevin
kklouzal
Posts: 343
Joined: Sun Mar 28, 2010 8:14 pm
Location: USA - Arizona

Re: How to release the memory created by SphereSceneNode?

Post by kklouzal »

I just want to ensure you followed tutorial 3 Custom SceneNode http://irrlicht.sourceforge.net/docu/example003.html
I'm done with my CSampleSceneNode object, and so must drop my reference. This won't delete the object, yet, because it is still attached to the scene graph, which prevents the deletion until the graph is deleted or the custom scene node is removed from it.

Code: Select all

    myNode->drop();
    myNode = 0; // As I shouldn't refer to it again, ensure that I can't
Remove() should remove the node from the scene graph assuming there are no active grabs on it. https://sledjhamr.org/source/src/others ... 9403b8b69c
You could also try addToDeletionQueue https://sledjhamr.org/source/src/others ... 8d7bf31f5a to acheive similar results however Remove() is the preferred method of use directly on scene nodes.

The only other thing I could suggest is that something fishy is going on due to the way you are creating your custom node.
The tutorial shows you should do it like this:

Code: Select all

    CSampleSceneNode *myNode =
        new CSampleSceneNode(smgr->getRootSceneNode(), smgr, 666);
Hope this helps!
Dream Big Or Go Home.
Help Me Help You.
CuteAlien
Admin
Posts: 9628
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: How to release the memory created by SphereSceneNode?

Post by CuteAlien »

Whenever a function name in Irrlicht starts with "create" you will have to call drop() on the returned pointer when you no longer need it. I don't see your code, so somewhat guessing: I suppose your node grabs the mesh when you put it in there. So the remove() of the node will only cause the node to drop it's reference. In general code with create functions looks like this:

Code: Select all

 
somePointer = createSomeStuff();
// someOtherClass should grab() the pointer when it receives it
someOtherClass->addSomehow(somePointer);
// now you no longer need somePointer yourself so you drop it
 somePointer->drop();
 
// ... some other place, for example when the application quits
// you will then get rid of someOtherClass, for example with remove() for nodes
// That class should then drop() it's pointer to somePointer.
 
edit: Meshes created by geometry creator are not in the mesh-cache.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
KevinLuo
Posts: 23
Joined: Sat Apr 15, 2017 6:08 am

Re: How to release the memory created by SphereSceneNode?

Post by KevinLuo »

Thanks for your reply! My scene node code is derived from tutorial 3. And my code is here:

Code: Select all

#pragma once
#include <irrlicht.h>
 
using namespace irr;
 
class CDynamicObject : public scene::ISceneNode
{
 
    core::aabbox3d<f32> Box;
    scene::IMeshSceneNode* sphereMesh;
    video::SMaterial Material;
    video::ITexture* m_textN, *m_textHigh,*m_textRight,*m_textWrong;
    video::ITexture* m_textSet[4];
    core::vector3df Bound[8];
    core::vector3df m_currentVec,m_currentPos,m_velDir;
    float m_vel;
    u32             m_timeNow;
    int m_textIndex;
    f32             m_radius;
 
public:
    bool            m_initFlag, m_setHighlight;
    bool            m_movingFlag;
    int m_ballIndex;
    CDynamicObject(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id,int index,core::vector3df *bound,core::vector3df _initVec,core::vector3df _initPos,f32 _radius)
        : scene::ISceneNode(parent, mgr, id)
    {
        m_ballIndex = index;
        m_radius = _radius;
        sphereMesh = SceneManager->addSphereSceneNode(m_radius,128,this,100);
        scene::ISceneNodeAnimator* anim = SceneManager->createRotationAnimator(_initVec);
        sphereMesh->addAnimator(anim);
        anim->drop();
        anim = 0;
 
        float boxRadius = m_radius / sqrt(3)*1.2;
        Box.MinEdge = core::vector3df(-boxRadius, -boxRadius, -boxRadius);
        Box.MaxEdge = core::vector3df(boxRadius, boxRadius, boxRadius);
        
 
        m_textN = SceneManager->getVideoDriver()->getTexture("orange.png");
        m_textHigh = SceneManager->getVideoDriver()->getTexture("highlight.png");
        m_textRight = SceneManager->getVideoDriver()->getTexture("right.png");
        m_textWrong = SceneManager->getVideoDriver()->getTexture("wrong.png");
        m_textSet[0] = m_textN;
        m_textSet[1] = m_textHigh;
        m_textSet[2] = m_textRight;
        m_textSet[3] = m_textWrong;
 
        m_textIndex = 0;
        sphereMesh->setMaterialTexture(0, m_textN);
        sphereMesh->setMaterialFlag(video::EMF_LIGHTING, true);
        sphereMesh->setMaterialType(video::EMT_SOLID);
        memcpy_s(Bound, sizeof(Bound), bound, sizeof(Bound));
        m_currentPos = _initPos;
        m_currentVec = _initVec;
        m_vel = m_currentVec.getLength();
        m_velDir = m_currentVec.normalize();
        m_timeNow = 0;
        m_initFlag = false;
        m_setHighlight = false;
    }
 
    ~CDynamicObject()
    {
 
    }
 
    virtual void OnRegisterSceneNode()
    {
        if (IsVisible)
            SceneManager->registerNodeForRendering(this);
 
        ISceneNode::OnRegisterSceneNode();
    }
 
    virtual void render()
    {
    
    }
    void checkBound()
    {
        if (m_currentPos.X <= Bound[0].X + m_radius)
        {
            m_currentVec.X *= -1;
            m_currentPos.X = Bound[0].X + m_radius;
        }
        if (m_currentPos.X >= Bound[3].X - m_radius)
        {
            m_currentVec.X *= -1;
            m_currentPos.X = Bound[3].X - m_radius;
        }
 
        if (m_currentPos.Y <= Bound[1].Y + m_radius)
        {
            m_currentVec.Y *= -1;
            m_currentPos.Y = Bound[1].Y + m_radius;
        }
        if (m_currentPos.Y >= Bound[0].Y - m_radius)
        {
            m_currentVec.Y *= -1;
            m_currentPos.Y = Bound[0].Y - m_radius;
        }
 
        if (m_currentPos.Z <= Bound[0].Z + m_radius)
        {
            m_currentVec.Z *= -1;
            m_currentPos.Z = Bound[0].Z + m_radius;
        }
        if (m_currentPos.Z >= Bound[5].Z - m_radius)
        {
            m_currentVec.Z *= -1;
            m_currentPos.Z = Bound[5].Z - m_radius;
        }
 
    }
    void setVec(core::vector3df _vec)
    {
        m_currentVec = _vec;
    }
    void setSpeed(float _speed)
    {
        m_vel = _speed;
        m_velDir = m_currentVec.normalize();
        m_currentVec = m_velDir * m_vel;
    }
    void setPos(core::vector3df _pos)
    {
        m_currentPos = _pos;
    }
    core::vector3df getVec()
    {
        return m_currentVec;
    }
    core::vector3df getPos()
    {
        return m_currentPos;
    }
    float getRadius()
    {
        return m_radius;
    }
    void updatePosition(f32 _dt)
    {
        m_currentPos = m_currentPos + m_currentVec * _dt;   
        
        checkBound();
    }
    void setHighlight(bool _flag)
    {
        m_setHighlight = _flag;
        if (_flag)
        {
            sphereMesh->setMaterialTexture(0, m_textHigh);
        }
        else
        {
            sphereMesh->setMaterialTexture(0, m_textN);
        }
    }
    void setTexture(int index)
    {
        if (index==m_textIndex)
        {
            return;
        }
        if (index>=0&&index<4)
        {
            m_textIndex = index;
            sphereMesh->setMaterialTexture(0, m_textSet[index]);
        }
    }
    int getTextureIndex()
    {
        return m_textIndex;
    }
    void onAnimate(u32 _timeNow)
    {
        if (m_initFlag)
        {
            f32 frameDeltaTime = (f32)(_timeNow - m_timeNow) / 1000.f;
            if (m_movingFlag)
            {
                updatePosition(frameDeltaTime);
            }
        }
        else
        {
            m_initFlag = true;
        }
        setPosition(m_currentPos);
        m_timeNow = _timeNow;
    }
 
    virtual const core::aabbox3d<f32>& getBoundingBox() const
    {
        return Box;
    }
 
    virtual u32 getMaterialCount() const
    {
        return 1;
    }
 
    virtual video::SMaterial& getMaterial(u32 i)
    {
        return Material;
    }
};
 
And here is my initializing and removing code for this class:

Code: Select all

//Clear the exiting balls of previous game level
//the ballMgr is an empty scene, but the parent node of all ball nodes.
//the coMgr processes all collision events between balls.
void ClearCurrentBalls(int ballCount, CDynamicObjManager* ballMgr, CCollisionMgr* &coMgr)
{
    if (ballCount)
    {
        ballMgr->removeAll();
        delete coMgr;
        coMgr = NULL;
    }
 
}
 
//Create new ball instances for next game level
void CreateNewBalls(int ballCount, CDynamicObject** objset, CDynamicObjManager* ballMgr, CCollisionMgr* &coMgr)
{
    for (size_t i = 0; i < ballCount; i++)
    {
//getRandVec3() will generate a random normalized vector3df
//getRandPos()will get a random position for the ball inside the bounding box
 
        objset[i] = new CDynamicObject(ballMgr, ballMgr->getSceneManager(), IDFlag_BallNode, i, Bound, getRandVec3(), getRandPos(Bound, BallRadius), BallRadius);
        objset[i]->m_movingFlag = false;
        objset[i]->setTexture(0);
        objset[i]->drop();
    }
    coMgr = new CCollisionMgr(objset, ballCount, ballMgr->getSceneManager());
//In case that the distance between balls may be below the doubled ball's radius
    coMgr->updateCollisionEvent();
}
 
And I didn't call the createSphereMesh() method, SceneManager->addSphereSceneNode() did.
CuteAlien
Admin
Posts: 9628
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: How to release the memory created by SphereSceneNode?

Post by CuteAlien »

That are code-snippets and you posted. And you left out a function doing the memory management (ballMgr->removeAll and maybe CCollisionMgr also does stuff). So can only comment on the parts you posted:

First it's a little confusing why your CDynamicObject is derived from ISceneNode _and_ does have a ISceneNode. And you call the node inside it "sphereMesh". The naming kinda looks like you really want a mesh there and not a node (which you could create with IGeometryCreator). Or maybe the other way round - CDynamicObject should not be derived from ISceneNode at all - the name it has kinda gives me the impression that it shouldn't be a node.

Next thing which looks strange is that you do objset->drop(); immediately after creating it. The idea behind reference-counting memory is - you keep your reference while you have an object working with that memory. So you would do objset->drop(); for example in your ballMgr->removeAll function. While you still use an object pointer you should never drop() it.

This is probably not yet your problem - I'm pretty sure that one is hidden in the code you did not post. So can't help with it so far.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
KevinLuo
Posts: 23
Joined: Sat Apr 15, 2017 6:08 am

Re: How to release the memory created by SphereSceneNode?

Post by KevinLuo »

CuteAlien wrote:That are code-snippets and you posted. And you left out a function doing the memory management (ballMgr->removeAll and maybe CCollisionMgr also does stuff). So can only comment on the parts you posted:

First it's a little confusing why your CDynamicObject is derived from ISceneNode _and_ does have a ISceneNode. And you call the node inside it "sphereMesh". The naming kinda looks like you really want a mesh there and not a node (which you could create with IGeometryCreator). Or maybe the other way round - CDynamicObject should not be derived from ISceneNode at all - the name it has kinda gives me the impression that it shouldn't be a node.

Next thing which looks strange is that you do objset->drop(); immediately after creating it. The idea behind reference-counting memory is - you keep your reference while you have an object working with that memory. So you would do objset->drop(); for example in your ballMgr->removeAll function. While you still use an object pointer you should never drop() it.

This is probably not yet your problem - I'm pretty sure that one is hidden in the code you did not post. So can't help with it so far.

Thanks for your reply! CCollisionMgr only calculates velocity and position of each object.

Yes, the naming of "sphereMesh" is quite confusing. Actually, the CDynamicObject is created to manage a sphere mesh with some physic properties. To my knowledge, I only know using addSphereSceneNode to add a sphere mesh scene.IGeometryCreator is new to me. And the CDynamicObject is derived form ISceneNode, because I want to update its velocity and position at OnAnimate function.

And about the drop() part, I just copy it from tutorial 3 CustomSceneNode. It drops MyNode immediately, so I thought this node has already been grabbed by the SceneManager, the reference count will be 1. So if I want to release these nodes, I just need to call the SceneManager or its parent to remove them, and their reference count would be 0, and their memory will be released.Am I right?

According to your suggestion, I may try the IGeometryCreator to create a real sphere mesh. Then I think I can release the memory manually.
CuteAlien
Admin
Posts: 9628
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: How to release the memory created by SphereSceneNode?

Post by CuteAlien »

KevinLuo wrote:IGeometryCreator is new to me.
You can access it from ISceneManager::getGeometryCreator.
KevinLuo wrote: And the CDynamicObject is derived form ISceneNode, because I want to update its velocity and position at OnAnimate function.
Makes sense. And once you use a mesh instead of another node inside your node it will be OK. Alternatively you can use animators which can be added to nodes.
KevinLuo wrote: And about the drop() part, I just copy it from tutorial 3 CustomSceneNode. It drops MyNode immediately, so I thought this node has already been grabbed by the SceneManager, the reference count will be 1. So if I want to release these nodes, I just need to call the SceneManager or its parent to remove them, and their reference count would be 0, and their memory will be released.Am I right?
This is right, but not how you should think. The idea behind reference counting is that you do not have care about how many reference does the object have left. But usually you keep your references around as long as you use that pointer. When you no longer need it then you drop() it. Not before. And you have to call drop() when you use "new" or when an Irrlicht function starts with "create". Still can't tell from your code what is going wrong, I still think you didn't post the part which causes it to get reference counting wrong.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
KevinLuo
Posts: 23
Joined: Sat Apr 15, 2017 6:08 am

Re: How to release the memory created by SphereSceneNode?

Post by KevinLuo »

Thanks, CuteAlien and kklouzal!

I finally solved this memory problem. At the very beginning, I just remove the CDynamicObejct instance I created, I
didn't drop their pointers. After seeing kklouzal's post, I added the pointers' drop to my code, and the memory might
not be released right after the remove method. So I thought the drop method didn't solve this, it turned out to be that
the memory is released but I couldn't see it from the debug tool immediately. Now I drop every pointer I created, then
call the removeall function. The memory won't be a problem anymore.

Special thanks to CuteAlien! I learned how to use the IGeometryCreator and it also worked.
Post Reply