Merge pull request #14887 from AndrewMeadows/shape-garbage-collector

Case 21066: restore shape caching in ShapeManager
This commit is contained in:
Shannon Romano 2019-02-14 17:33:33 -08:00 committed by GitHub
commit 3b5b077a3e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 141 additions and 93 deletions

View file

@ -6228,27 +6228,40 @@ void Application::update(float deltaTime) {
PROFILE_RANGE(simulation_physics, "PrePhysics"); PROFILE_RANGE(simulation_physics, "PrePhysics");
PerformanceTimer perfTimer("prePhysics)"); PerformanceTimer perfTimer("prePhysics)");
{ {
PROFILE_RANGE(simulation_physics, "RemoveEntities");
const VectorOfMotionStates& motionStates = _entitySimulation->getObjectsToRemoveFromPhysics(); const VectorOfMotionStates& motionStates = _entitySimulation->getObjectsToRemoveFromPhysics();
{
PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size());
_physicsEngine->removeObjects(motionStates); _physicsEngine->removeObjects(motionStates);
}
_entitySimulation->deleteObjectsRemovedFromPhysics(); _entitySimulation->deleteObjectsRemovedFromPhysics();
} }
{
PROFILE_RANGE(simulation_physics, "AddEntities");
VectorOfMotionStates motionStates; VectorOfMotionStates motionStates;
getEntities()->getTree()->withReadLock([&] { getEntities()->getTree()->withReadLock([&] {
_entitySimulation->getObjectsToAddToPhysics(motionStates); _entitySimulation->getObjectsToAddToPhysics(motionStates);
PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size());
_physicsEngine->addObjects(motionStates); _physicsEngine->addObjects(motionStates);
}); });
}
{
VectorOfMotionStates motionStates;
PROFILE_RANGE(simulation_physics, "ChangeEntities");
getEntities()->getTree()->withReadLock([&] { getEntities()->getTree()->withReadLock([&] {
_entitySimulation->getObjectsToChange(motionStates); _entitySimulation->getObjectsToChange(motionStates);
VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates); VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
_entitySimulation->setObjectsToChange(stillNeedChange); _entitySimulation->setObjectsToChange(stillNeedChange);
}); });
}
_entitySimulation->applyDynamicChanges(); _entitySimulation->applyDynamicChanges();
t1 = std::chrono::high_resolution_clock::now(); t1 = std::chrono::high_resolution_clock::now();
{
PROFILE_RANGE(simulation_physics, "Avatars");
PhysicsEngine::Transaction transaction; PhysicsEngine::Transaction transaction;
avatarManager->buildPhysicsTransaction(transaction); avatarManager->buildPhysicsTransaction(transaction);
_physicsEngine->processTransaction(transaction); _physicsEngine->processTransaction(transaction);
@ -6258,11 +6271,15 @@ void Application::update(float deltaTime) {
myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction); myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction);
myAvatar->prepareForPhysicsSimulation(); myAvatar->prepareForPhysicsSimulation();
_physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying()); _physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying());
}
{
PROFILE_RANGE(simulation_physics, "PrepareActions");
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) { _physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
dynamic->prepareForPhysicsSimulation(); dynamic->prepareForPhysicsSimulation();
}); });
} }
}
auto t2 = std::chrono::high_resolution_clock::now(); auto t2 = std::chrono::high_resolution_clock::now();
{ {
PROFILE_RANGE(simulation_physics, "StepPhysics"); PROFILE_RANGE(simulation_physics, "StepPhysics");

View file

@ -90,7 +90,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
} }
void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
PROFILE_RANGE(app, __FUNCTION__); PROFILE_RANGE(render, __FUNCTION__);
if (!_uiTexture) { if (!_uiTexture) {
_uiTexture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()); _uiTexture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda());
@ -119,7 +119,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
} }
void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
PROFILE_RANGE(app, __FUNCTION__); PROFILE_RANGE(render, __FUNCTION__);
gpu::Batch& batch = *renderArgs->_batch; gpu::Batch& batch = *renderArgs->_batch;
auto geometryCache = DependencyManager::get<GeometryCache>(); auto geometryCache = DependencyManager::get<GeometryCache>();
@ -176,7 +176,7 @@ static const auto DEFAULT_SAMPLER = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LI
static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
void ApplicationOverlay::buildFramebufferObject() { void ApplicationOverlay::buildFramebufferObject() {
PROFILE_RANGE(app, __FUNCTION__); PROFILE_RANGE(render, __FUNCTION__);
auto uiSize = glm::uvec2(qApp->getUiSize()); auto uiSize = glm::uvec2(qApp->getUiSize());
if (!_overlayFramebuffer || uiSize != _overlayFramebuffer->getSize()) { if (!_overlayFramebuffer || uiSize != _overlayFramebuffer->getSize()) {

View file

@ -17,7 +17,10 @@
#include "ShapeFactory.h" #include "ShapeFactory.h"
const int MAX_RING_SIZE = 256;
ShapeManager::ShapeManager() { ShapeManager::ShapeManager() {
_garbageRing.reserve(MAX_RING_SIZE);
} }
ShapeManager::~ShapeManager() { ShapeManager::~ShapeManager() {
@ -33,8 +36,8 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
if (info.getType() == SHAPE_TYPE_NONE) { if (info.getType() == SHAPE_TYPE_NONE) {
return nullptr; return nullptr;
} }
HashKey key = info.getHash(); HashKey hashKey(info.getHash());
ShapeReference* shapeRef = _shapeMap.find(key); ShapeReference* shapeRef = _shapeMap.find(hashKey);
if (shapeRef) { if (shapeRef) {
shapeRef->refCount++; shapeRef->refCount++;
return shapeRef->shape; return shapeRef->shape;
@ -44,23 +47,43 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
ShapeReference newRef; ShapeReference newRef;
newRef.refCount = 1; newRef.refCount = 1;
newRef.shape = shape; newRef.shape = shape;
newRef.key = key; newRef.key = info.getHash();
_shapeMap.insert(key, newRef); _shapeMap.insert(hashKey, newRef);
} }
return shape; return shape;
} }
// private helper method // private helper method
bool ShapeManager::releaseShapeByKey(const HashKey& key) { bool ShapeManager::releaseShapeByKey(uint64_t key) {
ShapeReference* shapeRef = _shapeMap.find(key); HashKey hashKey(key);
ShapeReference* shapeRef = _shapeMap.find(hashKey);
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); // look for existing entry in _garbageRing
const int MAX_SHAPE_GARBAGE_CAPACITY = 255; int32_t ringSize = (int32_t)(_garbageRing.size());
if (_pendingGarbage.size() > MAX_SHAPE_GARBAGE_CAPACITY) { for (int32_t i = 0; i < ringSize; ++i) {
collectGarbage(); int32_t j = (_ringIndex + ringSize) % ringSize;
if (_garbageRing[j] == key) {
// already on the list, don't add it again
return true;
}
}
if (ringSize == MAX_RING_SIZE) {
// remove one
HashKey hashKeyToRemove(_garbageRing[_ringIndex]);
ShapeReference* shapeRef = _shapeMap.find(hashKeyToRemove);
if (shapeRef && shapeRef->refCount == 0) {
ShapeFactory::deleteShape(shapeRef->shape);
_shapeMap.remove(hashKeyToRemove);
}
// replace at _ringIndex and advance
_garbageRing[_ringIndex] = key;
_ringIndex = (_ringIndex + 1) % ringSize;
} else {
// add one
_garbageRing.push_back(key);
} }
} }
return true; return true;
@ -87,21 +110,22 @@ bool ShapeManager::releaseShape(const btCollisionShape* shape) {
} }
void ShapeManager::collectGarbage() { void ShapeManager::collectGarbage() {
int numShapes = _pendingGarbage.size(); int numShapes = (int32_t)(_garbageRing.size());
for (int i = 0; i < numShapes; ++i) { for (int i = 0; i < numShapes; ++i) {
HashKey& key = _pendingGarbage[i]; HashKey key(_garbageRing[i]);
ShapeReference* shapeRef = _shapeMap.find(key); ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef && shapeRef->refCount == 0) { if (shapeRef && shapeRef->refCount == 0) {
ShapeFactory::deleteShape(shapeRef->shape); ShapeFactory::deleteShape(shapeRef->shape);
_shapeMap.remove(key); _shapeMap.remove(key);
} }
} }
_pendingGarbage.clear(); _ringIndex = 0;
_garbageRing.clear();
} }
int ShapeManager::getNumReferences(const ShapeInfo& info) const { int ShapeManager::getNumReferences(const ShapeInfo& info) const {
HashKey key = info.getHash(); HashKey hashKey(info.getHash());
const ShapeReference* shapeRef = _shapeMap.find(key); const ShapeReference* shapeRef = _shapeMap.find(hashKey);
if (shapeRef) { if (shapeRef) {
return shapeRef->refCount; return shapeRef->refCount;
} }

View file

@ -12,6 +12,8 @@
#ifndef hifi_ShapeManager_h #ifndef hifi_ShapeManager_h
#define hifi_ShapeManager_h #define hifi_ShapeManager_h
#include <vector>
#include <btBulletDynamicsCommon.h> #include <btBulletDynamicsCommon.h>
#include <LinearMath/btHashMap.h> #include <LinearMath/btHashMap.h>
@ -41,6 +43,7 @@
// later. When that list grows big enough the ShapeManager will remove any matching // later. When that list grows big enough the ShapeManager will remove any matching
// entries that still have zero ref-count. // entries that still have zero ref-count.
class ShapeManager { class ShapeManager {
public: public:
@ -63,19 +66,20 @@ public:
bool hasShape(const btCollisionShape* shape) const; bool hasShape(const btCollisionShape* shape) const;
private: private:
bool releaseShapeByKey(const HashKey& key); bool releaseShapeByKey(uint64_t key);
class ShapeReference { class ShapeReference {
public: public:
int refCount; int refCount;
const btCollisionShape* shape; const btCollisionShape* shape;
HashKey key; uint64_t key { 0 };
ShapeReference() : refCount(0), shape(nullptr) {} ShapeReference() : refCount(0), shape(nullptr) {}
}; };
// btHashMap is required because it supports memory alignment of the btCollisionShapes // btHashMap is required because it supports memory alignment of the btCollisionShapes
btHashMap<HashKey, ShapeReference> _shapeMap; btHashMap<HashKey, ShapeReference> _shapeMap;
btAlignedObjectArray<HashKey> _pendingGarbage; std::vector<uint64_t> _garbageRing;
uint32_t _ringIndex { 0 };
}; };
#endif // hifi_ShapeManager_h #endif // hifi_ShapeManager_h

View file

@ -32,17 +32,19 @@ class HashKey {
public: public:
static float getNumQuantizedValuesPerMeter(); static float getNumQuantizedValuesPerMeter();
HashKey() {}
HashKey(uint64_t hash) : _hash(hash) {}
// These two methods are required by btHashMap. // These two methods are required by btHashMap.
bool equals(const HashKey& other) const { return _hash == other._hash; } bool equals(const HashKey& other) const { return _hash == other._hash; }
int32_t getHash() const { return (int32_t)((uint32_t)_hash); } int32_t getHash() const { return (int32_t)((uint32_t)_hash); }
void clear() { _hash = _hashCount = 0; } // These methods for accumulating a hash.
bool isNull() const { return _hash == 0 && _hashCount == 0; }
void hashUint64(uint64_t data); void hashUint64(uint64_t data);
void hashFloat(float data); void hashFloat(float data);
void hashVec3(const glm::vec3& data); void hashVec3(const glm::vec3& data);
uint64_t getHash64() const { return _hash; } // for debug/test purposes uint64_t getHash64() const { return _hash; }
private: private:
uint64_t _hash { 0 }; uint64_t _hash { 0 };

View file

@ -13,6 +13,7 @@
#include <math.h> #include <math.h>
#include "HashKey.h"
#include "NumericalConstants.h" // for MILLIMETERS_PER_METER #include "NumericalConstants.h" // for MILLIMETERS_PER_METER
/**jsdoc /**jsdoc
@ -96,7 +97,7 @@ void ShapeInfo::clear() {
_sphereCollection.clear(); _sphereCollection.clear();
_halfExtents = glm::vec3(0.0f); _halfExtents = glm::vec3(0.0f);
_offset = glm::vec3(0.0f); _offset = glm::vec3(0.0f);
_hashKey.clear(); _hash64 = 0;
_type = SHAPE_TYPE_NONE; _type = SHAPE_TYPE_NONE;
} }
@ -131,14 +132,14 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
default: default:
break; break;
} }
_hashKey.clear(); _hash64 = 0;
} }
void ShapeInfo::setBox(const glm::vec3& halfExtents) { void ShapeInfo::setBox(const glm::vec3& halfExtents) {
_url = ""; _url = "";
_type = SHAPE_TYPE_BOX; _type = SHAPE_TYPE_BOX;
setHalfExtents(halfExtents); setHalfExtents(halfExtents);
_hashKey.clear(); _hash64 = 0;
} }
void ShapeInfo::setSphere(float radius) { void ShapeInfo::setSphere(float radius) {
@ -146,7 +147,7 @@ void ShapeInfo::setSphere(float radius) {
_type = SHAPE_TYPE_SPHERE; _type = SHAPE_TYPE_SPHERE;
radius = glm::max(radius, MIN_HALF_EXTENT); radius = glm::max(radius, MIN_HALF_EXTENT);
_halfExtents = glm::vec3(radius); _halfExtents = glm::vec3(radius);
_hashKey.clear(); _hash64 = 0;
} }
void ShapeInfo::setMultiSphere(const std::vector<glm::vec3>& centers, const std::vector<float>& radiuses) { void ShapeInfo::setMultiSphere(const std::vector<glm::vec3>& centers, const std::vector<float>& radiuses) {
@ -158,12 +159,12 @@ void ShapeInfo::setMultiSphere(const std::vector<glm::vec3>& centers, const std:
SphereData sphere = SphereData(centers[i], radiuses[i]); SphereData sphere = SphereData(centers[i], radiuses[i]);
_sphereCollection.push_back(sphere); _sphereCollection.push_back(sphere);
} }
_hashKey.clear(); _hash64 = 0;
} }
void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) { void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) {
_pointCollection = pointCollection; _pointCollection = pointCollection;
_hashKey.clear(); _hash64 = 0;
} }
void ShapeInfo::setCapsuleY(float radius, float cylinderHalfHeight) { void ShapeInfo::setCapsuleY(float radius, float cylinderHalfHeight) {
@ -172,12 +173,12 @@ void ShapeInfo::setCapsuleY(float radius, float cylinderHalfHeight) {
radius = glm::max(radius, MIN_HALF_EXTENT); radius = glm::max(radius, MIN_HALF_EXTENT);
cylinderHalfHeight = glm::max(cylinderHalfHeight, 0.0f); cylinderHalfHeight = glm::max(cylinderHalfHeight, 0.0f);
_halfExtents = glm::vec3(radius, cylinderHalfHeight + radius, radius); _halfExtents = glm::vec3(radius, cylinderHalfHeight + radius, radius);
_hashKey.clear(); _hash64 = 0;
} }
void ShapeInfo::setOffset(const glm::vec3& offset) { void ShapeInfo::setOffset(const glm::vec3& offset) {
_offset = offset; _offset = offset;
_hashKey.clear(); _hash64 = 0;
} }
uint32_t ShapeInfo::getNumSubShapes() const { uint32_t ShapeInfo::getNumSubShapes() const {
@ -269,20 +270,21 @@ float ShapeInfo::computeVolume() const {
return volume; return volume;
} }
const HashKey& ShapeInfo::getHash() const { uint64_t ShapeInfo::getHash() const {
// NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance. // NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance.
if (_hashKey.isNull() && _type != SHAPE_TYPE_NONE) { if (_hash64 == 0 && _type != SHAPE_TYPE_NONE) {
HashKey hashKey;
// The key is not yet cached therefore we must compute it. // The key is not yet cached therefore we must compute it.
_hashKey.hashUint64((uint64_t)_type); hashKey.hashUint64((uint64_t)_type);
if (_type == SHAPE_TYPE_MULTISPHERE) { if (_type == SHAPE_TYPE_MULTISPHERE) {
for (auto &sphereData : _sphereCollection) { for (auto &sphereData : _sphereCollection) {
_hashKey.hashVec3(glm::vec3(sphereData)); hashKey.hashVec3(glm::vec3(sphereData));
_hashKey.hashFloat(sphereData.w); hashKey.hashFloat(sphereData.w);
} }
} else if (_type != SHAPE_TYPE_SIMPLE_HULL) { } else if (_type != SHAPE_TYPE_SIMPLE_HULL) {
_hashKey.hashVec3(_halfExtents); hashKey.hashVec3(_halfExtents);
_hashKey.hashVec3(_offset); hashKey.hashVec3(_offset);
} else { } else {
// TODO: we could avoid hashing all of these points if we were to supply the ShapeInfo with a unique // TODO: we could avoid hashing all of these points if we were to supply the ShapeInfo with a unique
// descriptive string. Shapes that are uniquely described by their type and URL could just put their // descriptive string. Shapes that are uniquely described by their type and URL could just put their
@ -292,7 +294,7 @@ const HashKey& ShapeInfo::getHash() const {
const int numPoints = (int)points.size(); const int numPoints = (int)points.size();
for (int i = 0; i < numPoints; ++i) { for (int i = 0; i < numPoints; ++i) {
_hashKey.hashVec3(points[i]); hashKey.hashVec3(points[i]);
} }
} }
@ -300,23 +302,24 @@ const HashKey& ShapeInfo::getHash() const {
if (!url.isEmpty()) { if (!url.isEmpty()) {
QByteArray baUrl = url.toLocal8Bit(); QByteArray baUrl = url.toLocal8Bit();
uint32_t urlHash = qChecksum(baUrl.data(), baUrl.size()); uint32_t urlHash = qChecksum(baUrl.data(), baUrl.size());
_hashKey.hashUint64((uint64_t)urlHash); hashKey.hashUint64((uint64_t)urlHash);
} }
if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_SIMPLE_COMPOUND) { if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_SIMPLE_COMPOUND) {
uint64_t numHulls = (uint64_t)_pointCollection.size(); uint64_t numHulls = (uint64_t)_pointCollection.size();
_hashKey.hashUint64(numHulls); hashKey.hashUint64(numHulls);
} else if (_type == SHAPE_TYPE_MULTISPHERE) { } else if (_type == SHAPE_TYPE_MULTISPHERE) {
uint64_t numSpheres = (uint64_t)_sphereCollection.size(); uint64_t numSpheres = (uint64_t)_sphereCollection.size();
_hashKey.hashUint64(numSpheres); hashKey.hashUint64(numSpheres);
} else if (_type == SHAPE_TYPE_SIMPLE_HULL) { } else if (_type == SHAPE_TYPE_SIMPLE_HULL) {
_hashKey.hashUint64(1); hashKey.hashUint64(1);
} }
_hash64 = hashKey.getHash64();
} }
return _hashKey; return _hash64;
} }
void ShapeInfo::setHalfExtents(const glm::vec3& halfExtents) { void ShapeInfo::setHalfExtents(const glm::vec3& halfExtents) {
_halfExtents = glm::max(halfExtents, glm::vec3(MIN_HALF_EXTENT)); _halfExtents = glm::max(halfExtents, glm::vec3(MIN_HALF_EXTENT));
_hashKey.clear(); _hash64 = 0;
} }

View file

@ -18,8 +18,6 @@
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtx/norm.hpp> #include <glm/gtx/norm.hpp>
#include "HashKey.h"
const float MIN_SHAPE_OFFSET = 0.001f; // offsets less than 1mm will be ignored const float MIN_SHAPE_OFFSET = 0.001f; // offsets less than 1mm will be ignored
// Bullet has a mesh generation util for convex shapes that we used to // Bullet has a mesh generation util for convex shapes that we used to
@ -91,7 +89,7 @@ public:
float computeVolume() const; float computeVolume() const;
const HashKey& getHash() const; uint64_t getHash() const;
protected: protected:
void setHalfExtents(const glm::vec3& halfExtents); void setHalfExtents(const glm::vec3& halfExtents);
@ -102,7 +100,7 @@ protected:
TriangleIndices _triangleIndices; TriangleIndices _triangleIndices;
glm::vec3 _halfExtents = glm::vec3(0.0f); glm::vec3 _halfExtents = glm::vec3(0.0f);
glm::vec3 _offset = glm::vec3(0.0f); glm::vec3 _offset = glm::vec3(0.0f);
mutable HashKey _hashKey; mutable uint64_t _hash64;
ShapeType _type = SHAPE_TYPE_NONE; ShapeType _type = SHAPE_TYPE_NONE;
}; };

View file

@ -55,20 +55,20 @@ void ShapeInfoTests::testHashFunctions() {
// test sphere // test sphere
info.setSphere(radiusX); info.setSphere(radiusX);
++testCount; ++testCount;
HashKey key = info.getHash(); HashKey hashKey(info.getHash());
hashPtr = hashes.find(key); hashPtr = hashes.find(hashKey);
if (hashPtr) { if (hashPtr) {
std::cout << testCount << " hash collision sphere radius = " << radiusX std::cout << testCount << " hash collision sphere radius = " << radiusX
<< " h = 0x" << std::hex << key.getHash() << " : 0x" << *hashPtr << " h = 0x" << std::hex << hashKey.getHash() << " : 0x" << *hashPtr
<< std::dec << std::endl; << std::dec << std::endl;
++numCollisions; ++numCollisions;
assert(false); assert(false);
} else { } else {
hashes.insert(key, key.getHash()); hashes.insert(hashKey, hashKey.getHash());
} }
// track bit distribution counts to evaluate hash function randomness // track bit distribution counts to evaluate hash function randomness
for (int j = 0; j < NUM_HASH_BITS; ++j) { for (int j = 0; j < NUM_HASH_BITS; ++j) {
if (masks[j] & key.getHash()) { if (masks[j] & hashKey.getHash()) {
++bits[j]; ++bits[j];
} }
} }
@ -80,21 +80,21 @@ void ShapeInfoTests::testHashFunctions() {
// test box // test box
info.setBox(glm::vec3(radiusX, radiusY, radiusZ)); info.setBox(glm::vec3(radiusX, radiusY, radiusZ));
++testCount; ++testCount;
HashKey key = info.getHash(); HashKey hashKey(info.getHash());
hashPtr = hashes.find(key); hashPtr = hashes.find(hashKey);
if (hashPtr) { if (hashPtr) {
std::cout << testCount << " hash collision box dimensions = < " << radiusX std::cout << testCount << " hash collision box dimensions = < " << radiusX
<< ", " << radiusY << ", " << radiusZ << " >" << ", " << radiusY << ", " << radiusZ << " >"
<< " h = 0x" << std::hex << key.getHash() << " : 0x" << *hashPtr << " : 0x" << key.getHash64() << " h = 0x" << std::hex << hashKey.getHash() << " : 0x" << *hashPtr << " : 0x" << hashKey.getHash64()
<< std::dec << std::endl; << std::dec << std::endl;
++numCollisions; ++numCollisions;
assert(false); assert(false);
} else { } else {
hashes.insert(key, key.getHash()); hashes.insert(hashKey, hashKey.getHash());
} }
// track bit distribution counts to evaluate hash function randomness // track bit distribution counts to evaluate hash function randomness
for (int k = 0; k < NUM_HASH_BITS; ++k) { for (int k = 0; k < NUM_HASH_BITS; ++k) {
if (masks[k] & key.getHash()) { if (masks[k] & hashKey.getHash()) {
++bits[k]; ++bits[k];
} }
} }
@ -117,14 +117,14 @@ void ShapeInfoTests::testBoxShape() {
ShapeInfo info; ShapeInfo info;
glm::vec3 halfExtents(1.23f, 4.56f, 7.89f); glm::vec3 halfExtents(1.23f, 4.56f, 7.89f);
info.setBox(halfExtents); info.setBox(halfExtents);
HashKey key = info.getHash(); HashKey hashKey(info.getHash());
const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
QCOMPARE(shape != nullptr, true); QCOMPARE(shape != nullptr, true);
ShapeInfo otherInfo = info; ShapeInfo otherInfo = info;
HashKey otherKey = otherInfo.getHash(); HashKey otherKey = otherInfo.getHash();
QCOMPARE(key.getHash(), otherKey.getHash()); QCOMPARE(hashKey.getHash(), otherKey.getHash());
delete shape; delete shape;
} }
@ -133,14 +133,14 @@ void ShapeInfoTests::testSphereShape() {
ShapeInfo info; ShapeInfo info;
float radius = 1.23f; float radius = 1.23f;
info.setSphere(radius); info.setSphere(radius);
HashKey key = info.getHash(); HashKey hashKey = info.getHash();
const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
QCOMPARE(shape != nullptr, true); QCOMPARE(shape != nullptr, true);
ShapeInfo otherInfo = info; ShapeInfo otherInfo = info;
HashKey otherKey = otherInfo.getHash(); HashKey otherKey = otherInfo.getHash();
QCOMPARE(key.getHash(), otherKey.getHash()); QCOMPARE(hashKey.getHash(), otherKey.getHash());
delete shape; delete shape;
} }
@ -151,14 +151,14 @@ void ShapeInfoTests::testCylinderShape() {
float radius = 1.23f; float radius = 1.23f;
float height = 4.56f; float height = 4.56f;
info.setCylinder(radius, height); info.setCylinder(radius, height);
HashKey key = info.getHash(); HashKey hashKey(info.getHash());
btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
QCOMPARE(shape != nullptr, true); QCOMPARE(shape != nullptr, true);
ShapeInfo otherInfo = info; ShapeInfo otherInfo = info;
HashKey otherKey = otherInfo.getHash(); HashKey otherKey = otherInfo.getHash();
QCOMPARE(key.getHash(), otherKey.getHash()); QCOMPARE(hashKey.getHash(), otherKey.getHash());
delete shape; delete shape;
*/ */
@ -170,14 +170,14 @@ void ShapeInfoTests::testCapsuleShape() {
float radius = 1.23f; float radius = 1.23f;
float height = 4.56f; float height = 4.56f;
info.setCapsule(radius, height); info.setCapsule(radius, height);
HashKey key = info.getHash(); HashKey hashKey(info.getHash());
btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
QCOMPARE(shape != nullptr, true); QCOMPARE(shape != nullptr, true);
ShapeInfo otherInfo = info; ShapeInfo otherInfo = info;
HashKey otherKey = otherInfo.getHash(); HashKey otherKey = otherInfo.getHash();
QCOMPARE(key.getHash(), otherKey.getHash()); QCOMPARE(hashKey.getHash(), otherKey.getHash());
delete shape; delete shape;
*/ */