Object/Particle pool memory problem

Object/Particle pool memory problem
0.0 0

#1

Hey all,
I’m trying to make a good particle/object pool manager for myself, and it works good - until I restart the Scene and try to use it again… I’m a beginner at c++ so maybe it’s something easy I’m missing. Anyways, here are some more details…

Usage:

fxMgr = Fx::create();
fxMgr->retain();
greenStarPoolIdx = fxMgr->addPool(fxNode, Fx::FxType::GREEN_STAR, 2);

later

auto star = fxMgr->getGreenStar(greenStarPoolIdx);

(do something with the star)

I recently made it so that the Fx class inherits from Ref. I used to have it as a unique_ptr to avoind having to deal w/ memory management, when changed it to inherit Ref to let cocos handle it instead.
Since I called retain, I have this in my scene’s destructor (can’t use addChild on it since it doesn’t inherit Node):

fxMgr->release();

Inside the Fx class; (except for methods for getting particles and adding object pools):

virtual ~Fx();
Fx();
Vector<ParticlePool*> _pools; <- used have it as std::vector<ParticlePool*>
ParticlePool.h (also inherits from Ref now to use cocos's own reference counting)

int particleIdx;
Vector<Node*> _pool;
virtual ~ParticlePool();
ParticlePool();

public:
Node* nextItem();
void add(Node* item);
static ParticlePool* create();

particle pool destructor

ParticlePool::~ParticlePool()
{
	CCLOG("pool dtor");

	for (size_t i = 0; i < _pool.size(); i++)
	{
		_pool.at(i)->release();
	}
}

Now, to the problem
When going to another scene, using breakpts, I can see the destructors for the Fx- and ParticlePool class being called, but not my GreenStar class (which the pool is holding).

Edit: updated the constructor to call release on all the items, now the destructor is called for those classes too. But I thought cocos Vectors would do this automatically? I don’t need to delete _pool though, right?

I can use the fxManager once, then when I restart or exit and re-enter the scene and try to use it again, it’s "0x00000000 " when I’m about to use it (after a key press).
I just can’t understand. Even if it was a memory leak I was at least expecting it to work since I’m making a new object? I need help.


#2

How to create fxNode? Did retain() it or not use autorelease? It won’t destruct if it was retained.

greenStarPoolIdx = fxMgr->addPool(fxNode, Fx::FxType::GREEN_STAR, 2);

#3

fxNode is a SpriteBatchNode (for better performance if you need lots of stuff at once).
I make it like so:

fxNode = SpriteBatchNode::create("images/green_star.png");

and then

addChild(fxNode);

but I do not call retain() on it. Guess it’s autorelease then?


#4

It looks fine. I don’t know where is the problem exactly. can you post complete code here?


#5

I updated my post above with some new info. Is there something in particular you want to look at?


#6
ParticlePool::~ParticlePool()
{
	CCLOG("pool dtor");

	for (size_t i = 0; i < _pool.size(); i++)
	{
		_pool.at(i)->release();
	}
}

Don’t release() Nodes in Vector unless you call retain() explicitly before adding to vector. Please look into cocos2d::Vector class, the vector will retain the Ref* once adding to vector and release it before it is removed or the vector destruct.

I don’t know what’s reason of your problem exactly yet, but maybe the usage is incorrect in some where.


#7

Ah, right. I’ll keep that in mind.
Here’s the Pool class, notice anything wrong here?


ParticlePool::~ParticlePool()
{
	CCLOG("pool dtor");
}

ParticlePool::ParticlePool()
{
	particleIdx = 0;
}

ParticlePool* ParticlePool::create()
{
	auto obj = new (std::nothrow) ParticlePool();
	if (obj)
	{
		obj->autorelease();
	}
	else
	{
		delete obj;
		obj = nullptr;
	}
	return obj;
}

Node* ParticlePool::nextItem()
{
	auto p = _pool.at(particleIdx);

	// item is busy?
	if (p->isVisible())
	{
		return nullptr;
	}

	if (++particleIdx == _pool.size())
	{
		particleIdx = 0;
	}

	return p;
}

void ParticlePool::add(Node* item)
{
	_pool.pushBack(item);
}

#8

Sorry, I can’t point out the problem yet.


#9

Ok, just to rule this one out…

{
	Vector<GreenStar*> lst;

	for (size_t i = 0; i < 3; i++)
	{
		GreenStar* item = GreenStar::init();
		lst.pushBack(item);
	}
}

// <-- Should I expect the destructors for GreenStar class here?

I never see the destructors being called for the class I put into the pool vector. That can’t be right, can it?


#10

I just realized that I never called autorelease() on the GreenStar class (among many others) and that was the reason I never saw the destructors.
It never crossed my mind to do this because I read that cocos2d used ref. counting, and in my mind ref. counting would mean that the object would be destroyed if ref count would reach 0 - unless to call retain() on it… so is retain being called somewhere else then?


#11

I solved it. Still not sure how it works but the problem is gone at least.

I tried to move the problematic code to my onTouchBegan callback, and was surprised to see that the problem did not occur there; I could restart the state and it would still work, hmm.

Then I looked at how I set up my listeners. I had been using a keyboardlistener like so:

auto kbListener = EventListenerKeyboard::create();
kbListener->onKeyPressed = CC_CALLBACK_2(GameModeA::onKeyPress, this);
eventDispatcher->addEventListenerWithFixedPriority(kbListener, 1);

and touchListener like so:

auto touchListener = EventListenerTouchOneByOne::create();
touchListener->onTouchBegan = CC_CALLBACK_2(GameModeA::onTouchBegan, this);

What I needed to change in order for the kbListener to work was instead of using a fixed priority, do:

auto kbListener = EventListenerKeyboard::create();
kbListener->onKeyPressed = CC_CALLBACK_2(GameModeA::onKeyPress, this);
eventDispatcher->addEventListenerWithSceneGraphPriority(kbListener, this);

If anyone can explain why the fixed priority one caused problems for me, it’d be interesting to hear. But I’m glad it’s fixed. This was a sneaky one for sure.


#12

@kevinwu1024 has good advice, also SpriteBatchNode isn’t really needed much anymore unless we don’t auto-batch how you need or you are using objects that are not auto-batched by default.


#13

unless we don’t auto-batch how you need or you are using objects that are not auto-batched by default.

how does one know what is auto-batched and what is not?


#14

Sprites, Labels, UI elements to name a few. Cocos2d-x v3.0 release notes I think have more information as well. It has been a while since I have read that version.


#15

Ok, maybe I should read those too. Thanks!