ShapeManager can release shape by pointer

This commit is contained in:
Andrew Meadows 2015-03-13 16:36:18 -07:00
parent 2b08c20900
commit e4c6d49c4d
3 changed files with 90 additions and 25 deletions

View file

@ -21,7 +21,7 @@ ShapeManager::~ShapeManager() {
int numShapes = _shapeMap.size(); int numShapes = _shapeMap.size();
for (int i = 0; i < numShapes; ++i) { for (int i = 0; i < numShapes; ++i) {
ShapeReference* shapeRef = _shapeMap.getAtIndex(i); ShapeReference* shapeRef = _shapeMap.getAtIndex(i);
delete shapeRef->_shape; delete shapeRef->shape;
} }
_shapeMap.clear(); _shapeMap.clear();
} }
@ -40,26 +40,27 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
DoubleHashKey key = info.getHash(); DoubleHashKey key = info.getHash();
ShapeReference* shapeRef = _shapeMap.find(key); ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef) { if (shapeRef) {
shapeRef->_refCount++; shapeRef->refCount++;
return shapeRef->_shape; return shapeRef->shape;
} }
btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info); btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info);
if (shape) { if (shape) {
ShapeReference newRef; ShapeReference newRef;
newRef._refCount = 1; newRef.refCount = 1;
newRef._shape = shape; newRef.shape = shape;
newRef.key = key;
_shapeMap.insert(key, newRef); _shapeMap.insert(key, newRef);
} }
return shape; return shape;
} }
bool ShapeManager::releaseShape(const ShapeInfo& info) { // private helper method
DoubleHashKey key = info.getHash(); bool ShapeManager::releaseShape(const DoubleHashKey& key) {
ShapeReference* shapeRef = _shapeMap.find(key); ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef) { if (shapeRef) {
if (shapeRef->_refCount > 0) { if (shapeRef->refCount > 0) {
shapeRef->_refCount--; shapeRef->refCount--;
if (shapeRef->_refCount == 0) { if (shapeRef->refCount == 0) {
_pendingGarbage.push_back(key); _pendingGarbage.push_back(key);
const int MAX_GARBAGE_CAPACITY = 127; const int MAX_GARBAGE_CAPACITY = 127;
if (_pendingGarbage.size() > MAX_GARBAGE_CAPACITY) { if (_pendingGarbage.size() > MAX_GARBAGE_CAPACITY) {
@ -78,10 +79,19 @@ bool ShapeManager::releaseShape(const ShapeInfo& info) {
return false; return false;
} }
bool ShapeManager::releaseShape(const ShapeInfo& info) {
return releaseShape(info.getHash());
}
bool ShapeManager::releaseShape(const btCollisionShape* shape) { bool ShapeManager::releaseShape(const btCollisionShape* shape) {
ShapeInfo info; int numShapes = _shapeMap.size();
ShapeInfoUtil::collectInfoFromShape(shape, info); for (int i = 0; i < numShapes; ++i) {
return releaseShape(info); ShapeReference* shapeRef = _shapeMap.getAtIndex(i);
if (shape == shapeRef->shape) {
return releaseShape(shapeRef->key);
}
}
return false;
} }
void ShapeManager::collectGarbage() { void ShapeManager::collectGarbage() {
@ -89,8 +99,8 @@ void ShapeManager::collectGarbage() {
for (int i = 0; i < numShapes; ++i) { for (int i = 0; i < numShapes; ++i) {
DoubleHashKey& key = _pendingGarbage[i]; DoubleHashKey& key = _pendingGarbage[i];
ShapeReference* shapeRef = _shapeMap.find(key); ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef && shapeRef->_refCount == 0) { if (shapeRef && shapeRef->refCount == 0) {
delete shapeRef->_shape; delete shapeRef->shape;
_shapeMap.remove(key); _shapeMap.remove(key);
} }
} }
@ -101,7 +111,29 @@ int ShapeManager::getNumReferences(const ShapeInfo& info) const {
DoubleHashKey key = info.getHash(); DoubleHashKey key = info.getHash();
const ShapeReference* shapeRef = _shapeMap.find(key); const ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef) { if (shapeRef) {
return shapeRef->_refCount; return shapeRef->refCount;
} }
return -1; return 0;
}
int ShapeManager::getNumReferences(const btCollisionShape* shape) const {
int numShapes = _shapeMap.size();
for (int i = 0; i < numShapes; ++i) {
const ShapeReference* shapeRef = _shapeMap.getAtIndex(i);
if (shape == shapeRef->shape) {
return shapeRef->refCount;
}
}
return 0;
}
bool ShapeManager::hasShape(const btCollisionShape* shape) const {
int numShapes = _shapeMap.size();
for (int i = 0; i < numShapes; ++i) {
const ShapeReference* shapeRef = _shapeMap.getAtIndex(i);
if (shape == shapeRef->shape) {
return true;
}
}
return false;
} }

View file

@ -38,12 +38,17 @@ public:
// validation methods // validation methods
int getNumShapes() const { return _shapeMap.size(); } int getNumShapes() const { return _shapeMap.size(); }
int getNumReferences(const ShapeInfo& info) const; int getNumReferences(const ShapeInfo& info) const;
int getNumReferences(const btCollisionShape* shape) const;
bool hasShape(const btCollisionShape* shape) const;
private: private:
bool releaseShape(const DoubleHashKey& key);
struct ShapeReference { struct ShapeReference {
int _refCount; int refCount;
btCollisionShape* _shape; btCollisionShape* shape;
ShapeReference() : _refCount(0), _shape(NULL) {} DoubleHashKey key;
ShapeReference() : refCount(0), shape(NULL) {}
}; };
btHashMap<DoubleHashKey, ShapeReference> _shapeMap; btHashMap<DoubleHashKey, ShapeReference> _shapeMap;

View file

@ -21,10 +21,8 @@ void ShapeManagerTests::testShapeAccounting() {
ShapeInfo info; ShapeInfo info;
info.setBox(glm::vec3(1.0f, 1.0f, 1.0f)); info.setBox(glm::vec3(1.0f, 1.0f, 1.0f));
// NOTE: ShapeManager returns -1 as refcount when the shape is unknown,
// which is distinct from "known but with zero references"
int numReferences = shapeManager.getNumReferences(info); int numReferences = shapeManager.getNumReferences(info);
if (numReferences != -1) { if (numReferences != 0) {
std::cout << __FILE__ << ":" << __LINE__ std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: expected ignorant ShapeManager after initialization" << std::endl; << " ERROR: expected ignorant ShapeManager after initialization" << std::endl;
} }
@ -104,8 +102,7 @@ void ShapeManagerTests::testShapeAccounting() {
if (shapeManager.getNumShapes() != 0) { if (shapeManager.getNumShapes() != 0) {
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: expected zero shapes after release" << std::endl; std::cout << __FILE__ << ":" << __LINE__ << " ERROR: expected zero shapes after release" << std::endl;
} }
numReferences = shapeManager.getNumReferences(info); if (shapeManager.hasShape(shape)) {
if (numReferences != -1) {
std::cout << __FILE__ << ":" << __LINE__ std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: expected ignorant ShapeManager after garbage collection" << std::endl; << " ERROR: expected ignorant ShapeManager after garbage collection" << std::endl;
} }
@ -122,33 +119,64 @@ void ShapeManagerTests::testShapeAccounting() {
void ShapeManagerTests::addManyShapes() { void ShapeManagerTests::addManyShapes() {
ShapeManager shapeManager; ShapeManager shapeManager;
QVector<btCollisionShape*> shapes;
int numSizes = 100; int numSizes = 100;
float startSize = 1.0f; float startSize = 1.0f;
float endSize = 99.0f; float endSize = 99.0f;
float deltaSize = (endSize - startSize) / (float)numSizes; float deltaSize = (endSize - startSize) / (float)numSizes;
ShapeInfo info; ShapeInfo info;
for (int i = 0; i < numSizes; ++i) { for (int i = 0; i < numSizes; ++i) {
// make a sphere
float s = startSize + (float)i * deltaSize; float s = startSize + (float)i * deltaSize;
glm::vec3 scale(s, 1.23f + s, s - 0.573f); glm::vec3 scale(s, 1.23f + s, s - 0.573f);
info.setBox(0.5f * scale); info.setBox(0.5f * scale);
btCollisionShape* shape = shapeManager.getShape(info); btCollisionShape* shape = shapeManager.getShape(info);
shapes.push_back(shape);
if (!shape) { if (!shape) {
std::cout << __FILE__ << ":" << __LINE__ std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: i = " << i << " null box shape for scale = " << scale << std::endl; << " ERROR: i = " << i << " null box shape for scale = " << scale << std::endl;
} }
// make a box
float radius = 0.5f * s; float radius = 0.5f * s;
info.setSphere(radius); info.setSphere(radius);
shape = shapeManager.getShape(info); shape = shapeManager.getShape(info);
shapes.push_back(shape);
if (!shape) { if (!shape) {
std::cout << __FILE__ << ":" << __LINE__ std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: i = " << i << " null sphere shape for radius = " << radius << std::endl; << " ERROR: i = " << i << " null sphere shape for radius = " << radius << std::endl;
} }
} }
// verify shape count
int numShapes = shapeManager.getNumShapes(); int numShapes = shapeManager.getNumShapes();
if (numShapes != 2 * numSizes) { if (numShapes != 2 * numSizes) {
std::cout << __FILE__ << ":" << __LINE__ std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: expected numShapes = " << numSizes << " but found numShapes = " << numShapes << std::endl; << " ERROR: expected numShapes = " << numSizes << " but found numShapes = " << numShapes << std::endl;
} }
// release each shape by pointer
for (int i = 0; i < numShapes; ++i) {
btCollisionShape* shape = shapes[i];
bool success = shapeManager.releaseShape(shape);
if (!success) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: failed to release shape index " << i << std::endl;
break;
}
}
// verify zero references
for (int i = 0; i < numShapes; ++i) {
btCollisionShape* shape = shapes[i];
int numReferences = shapeManager.getNumReferences(shape);
if (numReferences != 0) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: expected zero references for shape " << i
<< " but refCount = " << numReferences << std::endl;
}
}
} }
void ShapeManagerTests::addBoxShape() { void ShapeManagerTests::addBoxShape() {