Index: source/gameengine/Expressions/Expression.cpp =================================================================== --- source/gameengine/Expressions/Expression.cpp (r‚vision 13929) +++ source/gameengine/Expressions/Expression.cpp (copie de travail) @@ -22,7 +22,9 @@ ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// - +#ifdef _DEBUG +//int gRefCountExpr; +#endif CExpression::CExpression()// : m_cached_calculate(NULL) { m_refcount = 1; Index: source/gameengine/Expressions/Value.cpp =================================================================== --- source/gameengine/Expressions/Value.cpp (r‚vision 13929) +++ source/gameengine/Expressions/Value.cpp (copie de travail) @@ -169,8 +169,29 @@ return pyname; } +/*#define CVALUE_DEBUG*/ +#ifdef CVALUE_DEBUG +int gRefCount; +struct SmartCValueRef +{ + CValue *m_ref; + int m_count; + SmartCValueRef(CValue *ref) + { + m_ref = ref; + m_count = gRefCount++; + } +}; +#include +std::vector gRefList; +#endif + +#ifdef _DEBUG +//int gRefCountValue; +#endif + CValue::CValue(PyTypeObject *T) : PyObjectPlus(T), #else @@ -186,6 +207,12 @@ */ { //debug(gRefCountValue++) // debugging +#ifdef _DEBUG + //gRefCountValue++; +#ifdef CVALUE_DEBUG + gRefList.push_back(SmartCValueRef(this)); +#endif +#endif } @@ -199,6 +226,18 @@ ClearProperties(); assertd (m_refcount==0); +#ifdef CVALUE_DEBUG + std::vector::iterator it; + for (it=gRefList.begin(); it!=gRefList.end(); it++) + { + if (it->m_ref == this) + { + *it = gRefList.back(); + gRefList.pop_back(); + break; + } + } +#endif } @@ -293,7 +332,7 @@ } // Add property at end of array - (*m_pNamedPropertyArray)[name] = ioProperty;//->Add(ioProperty); + (*m_pNamedPropertyArray)[name] = ioProperty->AddRef();//->Add(ioProperty); } @@ -356,10 +395,13 @@ if (m_pNamedPropertyArray == NULL) return false; - // Scan all properties, as soon as we find one with -> Remove it -// CValue* val = (*m_pNamedPropertyArray)[inName]; - if (m_pNamedPropertyArray->erase(inName)) return true; - + CValue* val = GetProperty(inName); + if (NULL != val) + { + val->Release(); + m_pNamedPropertyArray->erase(inName); + return true; + } return false; } @@ -379,7 +421,7 @@ !(it == m_pNamedPropertyArray->end());it++) { CValue* tmpval = (*it).second; - STR_String name = (*it).first; + //STR_String name = (*it).first; tmpval->Release(); } @@ -469,8 +511,9 @@ for ( std::map::iterator it = m_pNamedPropertyArray->begin(); !(it == m_pNamedPropertyArray->end());it++) { - - replica->SetProperty((*it).first,(*it).second->GetReplica()); + CValue *val = (*it).second->GetReplica(); + replica->SetProperty((*it).first,val); + val->Release(); } } @@ -489,10 +532,6 @@ } - - - - /*--------------------------------------------------------------------------------------------------------------------- Reference Counting ---------------------------------------------------------------------------------------------------------------------*/ @@ -504,6 +543,9 @@ // Increase global reference count, used to see at the end of the program // if all CValue-derived classes have been dereferenced to 0 //debug(gRefCountValue++); +#ifdef _DEBUG + //gRefCountValue++; +#endif m_refcount++; return this; } @@ -518,7 +560,9 @@ // Decrease global reference count, used to see at the end of the program // if all CValue-derived classes have been dereferenced to 0 //debug(gRefCountValue--); - +#ifdef _DEBUG + //gRefCountValue--; +#endif // Decrease local reference count, if it reaches 0 the object should be freed if (--m_refcount > 0) { @@ -546,6 +590,9 @@ m_refcount--; //debug(gRefCountValue--); +#ifdef _DEBUG + //gRefCountValue--; +#endif m_ValFlags.RefCountDisabled=true; } @@ -590,11 +637,14 @@ } else { result = GetProperty(identifiername); + if (result) + return result->AddRef(); } - if (result) - return result->AddRef(); - // warning here !!! - result = new CErrorValue(identifiername+" not found"); + if (!result) + { + // warning here !!! + result = new CErrorValue(identifiername+" not found"); + } return result; } @@ -717,7 +767,7 @@ oldprop->SetValue(vallie); } else { - SetProperty(attr,vallie->AddRef()); + SetProperty(attr,vallie); } vallie->Release(); } Index: source/gameengine/Converter/KX_ConvertSensors.cpp =================================================================== --- source/gameengine/Converter/KX_ConvertSensors.cpp (r‚vision 13929) +++ source/gameengine/Converter/KX_ConvertSensors.cpp (copie de travail) @@ -733,6 +733,8 @@ logicmgr->RegisterToSensor(gamecont,gamesensor); } } + // done with gamesensor + gamesensor->Release(); } sens=sens->next; Index: source/gameengine/Converter/KX_ConvertProperties.cpp =================================================================== --- source/gameengine/Converter/KX_ConvertProperties.cpp (r‚vision 13929) +++ source/gameengine/Converter/KX_ConvertProperties.cpp (copie de travail) @@ -105,8 +105,9 @@ // set a subproperty called 'timer' so that // we can register the replica of this property // at the time a game object is replicated (AddObjectActuator triggers this) - - timeval->SetProperty("timer",new CBoolValue(true)); + CValue *bval = new CBoolValue(true); + timeval->SetProperty("timer",bval); + bval->Release(); if (isInActiveLayer) { timemgr->AddTimeProperty(timeval); @@ -128,6 +129,8 @@ { scene->AddDebugProperty(gameobj,STR_String(prop->name)); } + // done with propval, release it + propval->Release(); } prop = prop->next; Index: source/gameengine/Converter/KX_ConvertActuators.cpp =================================================================== --- source/gameengine/Converter/KX_ConvertActuators.cpp (r‚vision 13929) +++ source/gameengine/Converter/KX_ConvertActuators.cpp (copie de travail) @@ -934,6 +934,8 @@ gameobj->AddActuator(baseact); converter->RegisterGameActuator(baseact, bact); + // done with baseact, release it + baseact->Release(); } bact = bact->next; Index: source/gameengine/Converter/KX_ConvertControllers.cpp =================================================================== --- source/gameengine/Converter/KX_ConvertControllers.cpp (r‚vision 13929) +++ source/gameengine/Converter/KX_ConvertControllers.cpp (copie de travail) @@ -174,6 +174,8 @@ gameobj->AddController(gamecontroller); converter->RegisterGameController(gamecontroller, bcontr); + //done with gamecontroller + gamecontroller->Release(); } bcontr = bcontr->next; Index: source/gameengine/Converter/BL_BlenderDataConversion.cpp =================================================================== --- source/gameengine/Converter/BL_BlenderDataConversion.cpp (r‚vision 13929) +++ source/gameengine/Converter/BL_BlenderDataConversion.cpp (copie de travail) @@ -1514,7 +1514,8 @@ default: break; } - + delete shapeprops; + delete smmaterial; } @@ -1599,7 +1600,8 @@ KX_Camera* gamecamera = gamecamera_from_bcamera(static_cast(ob->data), kxscene, converter); gameobj = gamecamera; - gamecamera->AddRef(); + //don't add a reference: the camera list in kxscene->m_cameras is not released at the end + //gamecamera->AddRef(); kxscene->AddCamera(gamecamera); break; @@ -1845,6 +1847,7 @@ vector vec_parent_child; CListValue* objectlist = kxscene->GetObjectList(); + CListValue* inactivelist = kxscene->GetInactiveList(); CListValue* parentlist = kxscene->GetRootParentList(); SCA_LogicManager* logicmgr = kxscene->GetLogicManager(); @@ -1852,7 +1855,7 @@ CListValue* logicbrick_conversionlist = new CListValue(); - SG_TreeFactory tf; + //SG_TreeFactory tf; // Convert actions to actionmap bAction *curAct; @@ -1990,19 +1993,35 @@ if (isInActiveLayer) { objectlist->Add(gameobj->AddRef()); - tf.Add(gameobj->GetSGNode()); + //tf.Add(gameobj->GetSGNode()); gameobj->NodeUpdateGS(0,true); gameobj->Bucketize(); } + else + { + //we must store this object otherwise it will be deleted + //at the end of this function if it is not a root object + inactivelist->Add(gameobj->AddRef()); + } if (converter->addInitFromFrame){ gameobj->NodeSetLocalPosition(posPrev); gameobj->NodeSetLocalOrientation(angor); } } - + /* Note about memory leak issues: + When a CValue derived class is created, m_refcount is initialized to 1 + so the class must be released after being used to make sure that it won't + hang in memory. If the object needs to be stored for a long time, + use AddRef() so that this Release() does not free the object. + Make sure that for any AddRef() there is a Release()!!!! + Do the same for any object derived from CValue, CExpression and NG_NetworkMessage + */ + if (gameobj) + gameobj->Release(); + base = base->next; } Index: source/gameengine/GameLogic/SCA_IObject.cpp =================================================================== --- source/gameengine/GameLogic/SCA_IObject.cpp (r‚vision 13929) +++ source/gameengine/GameLogic/SCA_IObject.cpp (copie de travail) @@ -99,6 +101,7 @@ void SCA_IObject::AddSensor(SCA_ISensor* act) { + act->AddRef(); m_sensors.push_back(act); } @@ -106,6 +109,7 @@ void SCA_IObject::AddController(SCA_IController* act) { + act->AddRef(); m_controllers.push_back(act); } @@ -113,6 +117,7 @@ void SCA_IObject::AddActuator(SCA_IActuator* act) { + act->AddRef(); m_actuators.push_back(act); } Index: source/gameengine/GameLogic/SCA_LogicManager.cpp =================================================================== --- source/gameengine/GameLogic/SCA_LogicManager.cpp (r‚vision 13929) +++ source/gameengine/GameLogic/SCA_LogicManager.cpp (copie de travail) @@ -51,6 +51,10 @@ SCA_LogicManager::~SCA_LogicManager() { + /* AddRef() is not used when the objects are added to m_mapStringToGameObjects + so Release() should not be used either. The memory leak big is fixed + in BL_ConvertBlenderObjects() + int numgameobj = m_mapStringToGameObjects.size(); for (int i = 0; i < numgameobj; i++) { @@ -58,8 +62,9 @@ assert(gameobjptr); if (gameobjptr) (*gameobjptr)->Release(); - + } + */ /*for (int i=0;i* controllerarray = *(m_sensorcontrollermap[i]); @@ -72,6 +77,8 @@ } m_eventmanagers.clear(); m_sensorcontrollermapje.clear(); + m_removedActuators.clear(); + m_activeActuators.clear(); } Index: source/gameengine/GameLogic/SCA_PropertyActuator.cpp =================================================================== --- source/gameengine/GameLogic/SCA_PropertyActuator.cpp (r‚vision 13929) +++ source/gameengine/GameLogic/SCA_PropertyActuator.cpp (copie de travail) @@ -90,12 +90,11 @@ if (oldprop) { oldprop->SetValue(newval); - newval->Release(); } else { propowner->SetProperty(m_propname,newval); } - + newval->Release(); break; } case KX_ACT_PROP_ADD: @@ -123,9 +122,11 @@ CValue* copyprop = m_sourceObj->GetProperty(m_exprtxt); if (copyprop) { + CValue *val = copyprop->GetReplica(); GetParent()->SetProperty( m_propname, - copyprop->GetReplica()); + val); + val->Release(); } } @@ -239,11 +240,12 @@ CValue* prop = GetParent()->FindIdentifier(nameArg); - if (prop) { + if (!prop->IsError()) { m_propname = nameArg; } else { ; /* not found ... */ } + prop->Release(); Py_Return; } Index: source/gameengine/GameLogic/SCA_KeyboardSensor.cpp =================================================================== --- source/gameengine/GameLogic/SCA_KeyboardSensor.cpp (r‚vision 13929) +++ source/gameengine/GameLogic/SCA_KeyboardSensor.cpp (copie de travail) @@ -212,6 +212,7 @@ newprop.SetLength(oldlength - 1); CStringValue * newstringprop = new CStringValue(newprop, m_targetprop); GetParent()->SetProperty(m_targetprop, newstringprop); + newstringprop->Release(); } } else { /* append */ @@ -219,6 +220,7 @@ STR_String newprop = tprop->GetText() + pchar; CStringValue * newstringprop = new CStringValue(newprop, m_targetprop); GetParent()->SetProperty(m_targetprop, newstringprop); + newstringprop->Release(); } } else { if (!IsDelete(keyIndex)) { @@ -227,6 +229,7 @@ STR_String newprop = pchar; CStringValue * newstringprop = new CStringValue(newprop, m_targetprop); GetParent()->SetProperty(m_targetprop, newstringprop); + newstringprop->Release(); } } } Index: source/gameengine/Ketsji/KX_Scene.cpp =================================================================== --- source/gameengine/Ketsji/KX_Scene.cpp (r‚vision 13929) +++ source/gameengine/Ketsji/KX_Scene.cpp (copie de travail) @@ -35,6 +35,7 @@ #pragma warning (disable : 4786) #endif //WIN32 + #include "KX_Scene.h" #include "MT_assert.h" @@ -78,7 +79,14 @@ #include "BL_SkinDeformer.h" #include "BL_DeformableGameObject.h" +// to get USE_BULLET! +#include "KX_ConvertPhysicsObject.h" +#ifdef USE_BULLET +#include "CcdPhysicsEnvironment.h" +#include "CcdPhysicsController.h" +#endif + void* KX_SceneReplicationFunc(SG_IObject* node,void* gameobj,void* scene) { KX_GameObject* replica = ((KX_Scene*)scene)->AddNodeReplicaObject(node,(KX_GameObject*)gameobj); @@ -124,6 +132,7 @@ m_objectlist = new CListValue(); m_parentlist = new CListValue(); m_lightlist= new CListValue(); + m_inactivelist = new CListValue(); m_euthanasyobjects = new CListValue(); m_delayReleaseObjects = new CListValue(); @@ -183,6 +192,9 @@ if (m_parentlist) m_parentlist->Release(); + if (m_inactivelist) + m_inactivelist->Release(); + if (m_lightlist) m_lightlist->Release(); @@ -210,13 +222,40 @@ { delete m_bucketmanager; } - +#ifdef USE_BULLET + // This is a fix for memory leaks in bullet: the collision shapes is not destroyed + // when the physical controllers are destroyed. The reason is that shapes are shared + // between replicas of an object. There is no reference count in Bullet so the + // only workaround that does not involve changes in Bullet is to save in this array + // the list of shapes that are created when the scene is created (see KX_ConvertPhysicsObjects.cpp) + class btCollisionShape* shape; + class btTriangleMeshShape* meshShape; + vector::iterator it = m_shapes.begin(); + while (it != m_shapes.end()) { + shape = *it; + if (shape->getShapeType() == TRIANGLE_MESH_SHAPE_PROXYTYPE) + { + meshShape = static_cast(shape); + // shapes based on meshes use an interface that contains the vertices. + // Again the idea is to be able to share the interface between shapes but + // this is not used in Blender: each base object will have its own interface + btStridingMeshInterface* meshInterface = meshShape->getMeshInterface(); + if (meshInterface) + delete meshInterface; + } + delete shape; + it++; + } +#endif //Py_DECREF(m_attrlist); } +void KX_Scene::AddShape(class btCollisionShape*shape) +{ + m_shapes.push_back(shape); +} - void KX_Scene::SetProjectionMatrix(MT_CmMatrix4x4& pmat) { m_projectionmat = pmat; @@ -243,8 +282,13 @@ return m_parentlist; } +CListValue* KX_Scene::GetInactiveList() +{ + return m_inactivelist; +} + CListValue* KX_Scene::GetLightList() { return m_lightlist; @@ -415,7 +459,7 @@ replicanode->SetSGClientObject(newobj); // this is the list of object that are send to the graphics pipeline - m_objectlist->Add(newobj); + m_objectlist->Add(newobj->AddRef()); newobj->Bucketize(); // logic cannot be replicated, until the whole hierarchy is replicated. @@ -571,7 +615,9 @@ // add a timebomb to this object // for now, convert between so called frames and realtime m_tempObjectList->Add(replica->AddRef()); - replica->SetProperty("::timebomb",new CFloatValue(lifespan*0.02)); + CValue *fval = new CFloatValue(lifespan*0.02); + replica->SetProperty("::timebomb",fval); + fval->Release(); } // add to 'rootparent' list (this is the list of top hierarchy objects, updated each frame) @@ -634,7 +680,7 @@ replica->GetSGNode()->UpdateWorldData(0); replica->GetSGNode()->SetBBox(originalobj->GetSGNode()->BBox()); replica->GetSGNode()->SetRadius(originalobj->GetSGNode()->Radius()); - + // don't release replica here because we are returning it, not done with it... return replica; } @@ -654,7 +700,8 @@ // recursively destruct node->Destruct(); } - newobj->SetSGNode(0); + //no need to do that: the object is destroyed and memory released + //newobj->SetSGNode(0); } void KX_Scene::DelayedReleaseObject(CValue* gameobj) @@ -704,6 +751,7 @@ { m_logicmgr->RemoveDestroyedActuator(*ita); } + // the sensors/controllers/actuators must also be released, this is done in ~SCA_IObject // now remove the timer properties from the time manager int numprops = newobj->GetPropertyCount(); @@ -724,12 +772,15 @@ newobj->Release(); if (m_parentlist->RemoveValue(newobj)) newobj->Release(); + if (m_inactivelist->RemoveValue(newobj)) + newobj->Release(); if (m_euthanasyobjects->RemoveValue(newobj)) newobj->Release(); if (newobj == m_active_camera) { - m_active_camera->Release(); + //no AddRef done on m_active_camera so no Release + //m_active_camera->Release(); m_active_camera = NULL; } } @@ -1108,6 +1159,8 @@ for (i = numobj - 1; i >= 0; i--) { KX_GameObject* gameobj = (KX_GameObject*)m_euthanasyobjects->GetValue(i); + // KX_Scene::RemoveObject will also remove the object from this list + // that's why we start from the end this->RemoveObject(gameobj); } @@ -1115,11 +1168,11 @@ for (i = numobj-1;i>=0;i--) { KX_GameObject* gameobj = (KX_GameObject*)m_delayReleaseObjects->GetValue(i); - m_delayReleaseObjects->RemoveValue(gameobj); - + // This list is not for object removal, but just object release + gameobj->Release(); } - - + // empty the list as we have removed all references + m_delayReleaseObjects->Resize(0); } Index: source/gameengine/Ketsji/KX_KetsjiEngine.cpp =================================================================== --- source/gameengine/Ketsji/KX_KetsjiEngine.cpp (r‚vision 13929) +++ source/gameengine/Ketsji/KX_KetsjiEngine.cpp (copie de travail) @@ -1047,6 +1047,8 @@ scene->SetActiveCamera(activecam); scene->GetObjectList()->Add(activecam->AddRef()); scene->GetRootParentList()->Add(activecam->AddRef()); + //done with activecam + activecam->Release(); } scene->UpdateParents(0.0); Index: source/gameengine/Ketsji/KX_SCA_AddObjectActuator.cpp =================================================================== --- source/gameengine/Ketsji/KX_SCA_AddObjectActuator.cpp (r‚vision 13929) +++ source/gameengine/Ketsji/KX_SCA_AddObjectActuator.cpp (copie de travail) @@ -326,6 +326,8 @@ m_lastCreatedObject = replica; m_lastCreatedObject->AddRef(); + // finished using replica? then release it + replica->Release(); } } Index: source/gameengine/Ketsji/KX_Camera.cpp =================================================================== --- source/gameengine/Ketsji/KX_Camera.cpp (r‚vision 13929) +++ source/gameengine/Ketsji/KX_Camera.cpp (copie de travail) @@ -58,7 +58,9 @@ m_name = "cam"; m_projection_matrix.setIdentity(); m_modelview_matrix.setIdentity(); - SetProperty("camera",new CIntValue(1)); + CValue* val = new CIntValue(1); + SetProperty("camera",val); + val->Release(); } Index: source/gameengine/Ketsji/KX_ConvertPhysicsObjects.cpp =================================================================== --- source/gameengine/Ketsji/KX_ConvertPhysicsObjects.cpp (r‚vision 13929) +++ source/gameengine/Ketsji/KX_ConvertPhysicsObjects.cpp (copie de travail) @@ -858,6 +858,8 @@ //concaveShape = new btTriangleMeshShape( collisionMeshData ); concaveShape->recalcLocalAabb(); + if (collisionMeshShape) + delete collisionMeshShape; collisionMeshShape = concaveShape; } @@ -866,8 +868,10 @@ return collisionMeshShape; } - - delete collisionMeshShape; + if (collisionMeshShape) + delete collisionMeshShape; + if (collisionMeshData) + delete collisionMeshData; return NULL; } @@ -1021,7 +1025,10 @@ // ci.m_localInertiaTensor.setValue(0.1f,0.1f,0.1f); if (!bm) + { + delete motionstate; return; + } bm->setMargin(0.06); @@ -1057,6 +1064,7 @@ compoundShape->addChildShape(childTrans,bm); + kxscene->AddShape(bm); //do some recalc? //recalc inertia for rigidbody if (!rigidbody->isStaticOrKinematicObject()) @@ -1076,6 +1084,9 @@ btTransform identTrans; identTrans.setIdentity(); compoundShape->addChildShape(identTrans,bm); + //note abount compoundShape: Bullet does not delete the child shapes when + //the compound shape is deleted, so insert also the child shapes + kxscene->AddShape(bm); bm = compoundShape; } @@ -1112,9 +1123,6 @@ ci.m_collisionShape = bm; - - - ci.m_friction = smmaterial->m_friction;//tweak the friction a bit, so the default 0.5 works nice ci.m_restitution = smmaterial->m_restitution; ci.m_physicsEnv = env; @@ -1123,6 +1131,8 @@ ci.m_collisionFilterMask = (isbulletdyna) ? short(CcdConstructionInfo::AllFilter) : short(CcdConstructionInfo::AllFilter ^ CcdConstructionInfo::StaticFilter); KX_BulletPhysicsController* physicscontroller = new KX_BulletPhysicsController(ci,isbulletdyna); + //remember that we created a shape so that we can delete it when the scene is removed (bullet will not delete it) + kxscene->AddShape(bm); if (objprop->m_in_active_layer) { Index: source/gameengine/Ketsji/KX_Scene.h =================================================================== --- source/gameengine/Ketsji/KX_Scene.h (r‚vision 13929) +++ source/gameengine/Ketsji/KX_Scene.h (copie de travail) @@ -85,6 +85,7 @@ class RAS_IRasterizer; class RAS_IRenderTools; class SCA_JoystickManager; +class btCollisionShape; /** * The KX_Scene holds all data for an independent scene. It relates * KX_Objects to the specific objects in the modules. @@ -111,6 +112,7 @@ CListValue* m_objectlist; CListValue* m_parentlist; // all 'root' parents CListValue* m_lightlist; + CListValue* m_inactivelist; // all objects that are not in the active layer /** * The tree of objects in the scene. @@ -121,8 +123,12 @@ * The set of cameras for this scene */ list m_cameras; - /** + * The set of bullet shapes that must be deleted at the end of the scene + * to avoid memory leak (not deleted by bullet because shape are shared between replicas) + */ + vector m_shapes; + /** * Various SCA managers used by the scene */ SCA_LogicManager* m_logicmgr; @@ -300,6 +306,7 @@ void NewRemoveObject(CValue* gameobj); void ReplaceMesh(CValue* gameobj, void* meshobj); + void AddShape(class btCollisionShape* shape); /** * @section Logic stuff * Initiate an update of the logic system. @@ -316,6 +323,10 @@ ); CListValue* + GetInactiveList( + ); + + CListValue* GetRootParentList( );