From 932de57d4c00d182f773feb478467c26229d6887 Mon Sep 17 00:00:00 2001 From: "erwin.coumans" Date: Mon, 1 Sep 2008 18:46:57 +0000 Subject: [PATCH] Updated CDTestFramework with the OPCODE Array SAP test. Thanks Pierre Terdiman for the latest update. --- Extras/CDTestFramework/Bin/Opcode.dll | Bin 425984 -> 438272 bytes .../BulletSAPCompleteBoxPruningTest.cpp | 9 +- .../BulletSAPCompleteBoxPruningTest.h | 1 + Extras/CDTestFramework/CDTestFramework.cpp | 25 +- Extras/CDTestFramework/CDTestFramework.vcproj | 31 +- Extras/CDTestFramework/CompleteBoxPruning.cpp | 4 +- Extras/CDTestFramework/Opcode/Ice/IceAABB.h | 78 +- .../Opcode/Ice/IceAllocator.cpp | 805 +++++++++++ .../CDTestFramework/Opcode/Ice/IceAllocator.h | 52 + Extras/CDTestFramework/Opcode/Ice/IceAssert.h | 48 + .../Opcode/Ice/IceBitArray.cpp | 73 + .../CDTestFramework/Opcode/Ice/IceBitArray.h | 82 ++ .../Opcode/Ice/IceContainer.cpp | 110 +- .../CDTestFramework/Opcode/Ice/IceContainer.h | 88 +- Extras/CDTestFramework/Opcode/Ice/IceFPU.h | 108 +- .../CDTestFramework/Opcode/Ice/IceHashing.h | 78 ++ .../Opcode/Ice/IceMemoryMacros.h | 123 +- .../Opcode/Ice/IcePreprocessor.h | 90 +- .../Opcode/Ice/IceRevisitedRadix.cpp | 188 ++- .../Opcode/Ice/IceRevisitedRadix.h | 33 +- Extras/CDTestFramework/Opcode/Ice/IceTypes.h | 84 +- Extras/CDTestFramework/Opcode/Ice/IceUtils.h | 181 ++- Extras/CDTestFramework/Opcode/Ice/_IceAABB.h | 522 +++++++ .../Opcode/Ice/_IceContainer.cpp | 361 +++++ .../Opcode/Ice/_IceContainer.h | 228 +++ Extras/CDTestFramework/Opcode/Ice/_IceFPU.h | 333 +++++ .../Opcode/Ice/_IceMemoryMacros.h | 121 ++ .../Opcode/Ice/_IcePreprocessor.h | 144 ++ .../Opcode/Ice/_IceRevisitedRadix.cpp | 536 ++++++++ .../Opcode/Ice/_IceRevisitedRadix.h | 81 ++ Extras/CDTestFramework/Opcode/Ice/_IceTypes.h | 173 +++ Extras/CDTestFramework/Opcode/Ice/_IceUtils.h | 272 ++++ .../CDTestFramework/Opcode/OPC_ArraySAP.cpp | 1221 +++++++++++++++++ Extras/CDTestFramework/Opcode/OPC_ArraySAP.h | 159 +++ .../CDTestFramework/Opcode/OPC_BoxPruning.cpp | 6 +- Extras/CDTestFramework/Opcode/OPC_IceHook.h | 3 + Extras/CDTestFramework/Opcode/Opcode.h | 1 + Extras/CDTestFramework/Opcode/Opcode.vcproj | 34 +- Extras/CDTestFramework/OpcodeArraySAPTest.cpp | 210 +++ Extras/CDTestFramework/OpcodeArraySAPTest.h | 53 + Extras/CDTestFramework/ReadMe.txt | 46 +- 41 files changed, 6385 insertions(+), 410 deletions(-) create mode 100644 Extras/CDTestFramework/Opcode/Ice/IceAllocator.cpp create mode 100644 Extras/CDTestFramework/Opcode/Ice/IceAllocator.h create mode 100644 Extras/CDTestFramework/Opcode/Ice/IceAssert.h create mode 100644 Extras/CDTestFramework/Opcode/Ice/IceBitArray.cpp create mode 100644 Extras/CDTestFramework/Opcode/Ice/IceBitArray.h create mode 100644 Extras/CDTestFramework/Opcode/Ice/IceHashing.h create mode 100644 Extras/CDTestFramework/Opcode/Ice/_IceAABB.h create mode 100644 Extras/CDTestFramework/Opcode/Ice/_IceContainer.cpp create mode 100644 Extras/CDTestFramework/Opcode/Ice/_IceContainer.h create mode 100644 Extras/CDTestFramework/Opcode/Ice/_IceFPU.h create mode 100644 Extras/CDTestFramework/Opcode/Ice/_IceMemoryMacros.h create mode 100644 Extras/CDTestFramework/Opcode/Ice/_IcePreprocessor.h create mode 100644 Extras/CDTestFramework/Opcode/Ice/_IceRevisitedRadix.cpp create mode 100644 Extras/CDTestFramework/Opcode/Ice/_IceRevisitedRadix.h create mode 100644 Extras/CDTestFramework/Opcode/Ice/_IceTypes.h create mode 100644 Extras/CDTestFramework/Opcode/Ice/_IceUtils.h create mode 100644 Extras/CDTestFramework/Opcode/OPC_ArraySAP.cpp create mode 100644 Extras/CDTestFramework/Opcode/OPC_ArraySAP.h create mode 100644 Extras/CDTestFramework/OpcodeArraySAPTest.cpp create mode 100644 Extras/CDTestFramework/OpcodeArraySAPTest.h diff --git a/Extras/CDTestFramework/Bin/Opcode.dll b/Extras/CDTestFramework/Bin/Opcode.dll index edc82f030bda6ee0d3bfb0c30346e3e7a7e9691a..bdb17d72c4084e2452150c16f16332608dc6fc38 100644 GIT binary patch delta 48562 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 delta 33521 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 diff --git a/Extras/CDTestFramework/BulletSAPCompleteBoxPruningTest.cpp b/Extras/CDTestFramework/BulletSAPCompleteBoxPruningTest.cpp index 14744c7d9..b92d03d42 100644 --- a/Extras/CDTestFramework/BulletSAPCompleteBoxPruningTest.cpp +++ b/Extras/CDTestFramework/BulletSAPCompleteBoxPruningTest.cpp @@ -342,7 +342,8 @@ BulletSAPCompleteBoxPruningTest::BulletSAPCompleteBoxPruningTest(int numBoxes,in mBoxPtrs (null), mBoxTime (null), mSpeed (0.005f), - mAmplitude (100.0f) + mAmplitude (100.0f), + m_method(method) { btVector3 aabbMin(-200,-200,-200); btVector3 aabbMax(200,200,200); @@ -412,7 +413,7 @@ BulletSAPCompleteBoxPruningTest::BulletSAPCompleteBoxPruningTest(int numBoxes,in case 7: m_broadphase = new btDbvtBroadphase(); m_isdbvt = true; - methodname = "btDbvtBroadphase"; + methodname = "dynamic AABB tree, btDbvtBroadphase"; break; default: { @@ -660,8 +661,8 @@ void BulletSAPCompleteBoxPruningTest::PerformTest() }*/ char Buffer[4096]; - sprintf(Buffer, "CompleteBoxPruning: %5.1f us (%d cycles) : %d pairs\n", mProfiler.mMsTime, mProfiler.mCycles, - m_broadphase->getOverlappingPairCache()->getNumOverlappingPairs()); + sprintf(Buffer, "Bullet %s: %5.1f us (%d cycles) : %d pairs\n", methodname, mProfiler.mMsTime, mProfiler.mCycles, + m_broadphase->getOverlappingPairCache()->getNumOverlappingPairs()); // m_broadphase)->printStats(); diff --git a/Extras/CDTestFramework/BulletSAPCompleteBoxPruningTest.h b/Extras/CDTestFramework/BulletSAPCompleteBoxPruningTest.h index fbe78b22a..db2ba4a16 100644 --- a/Extras/CDTestFramework/BulletSAPCompleteBoxPruningTest.h +++ b/Extras/CDTestFramework/BulletSAPCompleteBoxPruningTest.h @@ -57,6 +57,7 @@ subject to the following restrictions: float mSpeed; float mAmplitude; bool m_firstTime; + int m_method; private: bool UpdateBoxes(int numBoxes); diff --git a/Extras/CDTestFramework/CDTestFramework.cpp b/Extras/CDTestFramework/CDTestFramework.cpp index 4f810c9ca..c9415355e 100644 --- a/Extras/CDTestFramework/CDTestFramework.cpp +++ b/Extras/CDTestFramework/CDTestFramework.cpp @@ -22,13 +22,14 @@ subject to the following restrictions: #include "CompleteBoxPruning.h" #include "BulletSAPCompleteBoxPruningTest.h" #include "BipartiteBoxPruning.h" +#include "OpcodeArraySAPTest.h" #include "RenderingHelpers.h" #include "Terrain.h" #include "Camera.h" #include "GLFontRenderer.h" -#include "BulletCollision/BroadphaseCollision/btDbvt.h" #define NUM_SAP_BOXES 8192 +//#define NUM_SAP_BOXES 1024 int percentUpdate = 10; @@ -62,11 +63,12 @@ enum TestIndex // TEST_COMPLETE_BOX_PRUNING=0, TEST_COMPLETE_BOX_PRUNING_8192, // TEST_BULLET_SAP_1024, - // TEST_BULLET_SAP_8192, + TEST_BULLET_SAP_8192, // TEST_BULLET_SAP_SORTEDPAIRS_8192, TEST_BULLET_MULTISAP_8192, // TEST_BIPARTITE_BOX_PRUNING, TEST_DBVT_8192, + TEST_OPCODE_ARRAY_SAP, MAX_NB_TESTS }; @@ -225,15 +227,6 @@ static void Terminate() int main(int argc, char** argv) { - { - ::SetPriorityClass(::GetCurrentProcess(),HIGH_PRIORITY_CLASS); - ::SetThreadPriority(::GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL); - #if 0 - btDbvt::benchmark(); - exit(0); - #endif - } - // Initialize AntTweakBar // (note that AntTweakBar could also be intialize after GLUT, no matter) if(!TwInit(TW_OPENGL, NULL)) @@ -296,13 +289,14 @@ int main(int argc, char** argv) // {TEST_OBB_MESH_QUERY, "OBB-mesh query"}, // {TEST_CAPSULE_MESH_QUERY, "Capsule-mesh query"}, // {TEST_COMPLETE_BOX_PRUNING, "OPCODE SAP 1024"}, - {TEST_COMPLETE_BOX_PRUNING_8192, "OPCODE SAP 8192"}, + {TEST_COMPLETE_BOX_PRUNING_8192, "OPCODE BOX PRUNING 8192"}, // {TEST_BULLET_SAP_1024, "Bullet SAP HASHPAIR 1024"}, - // {TEST_BULLET_SAP_8192, "Bullet SAP HASHPAIR 8192"}, + {TEST_BULLET_SAP_8192, "Bullet SAP HASHPAIR 8192"}, // {TEST_BULLET_SAP_SORTEDPAIRS_8192, "Bullet SAP SORTEDPAIR 8192"}, {TEST_BULLET_MULTISAP_8192, "Bullet MultiSAP 8192"}, // {TEST_BIPARTITE_BOX_PRUNING, "Bipartite box pruning"}, - {TEST_DBVT_8192, "DBVT 8192"}, + {TEST_DBVT_8192, "Bullet DBVT 8192"}, + {TEST_OPCODE_ARRAY_SAP, "OPCODE ARRAY SAP"}, }; TwType testType = TwDefineEnum("CollisionTest", testEV, MAX_NB_TESTS); TwAddVarRW(gMainBar, "CollisionTests", testType, &gSelectedTest, ""); @@ -318,11 +312,12 @@ int main(int argc, char** argv) // gCollisionTests[TEST_COMPLETE_BOX_PRUNING_8192] = new CompleteBoxPruningTest(NUM_SAP_BOXES); gCollisionTests[TEST_COMPLETE_BOX_PRUNING_8192] = new CompleteBoxPruningTest(NUM_SAP_BOXES); // gCollisionTests[TEST_BULLET_SAP_1024] = new BulletSAPCompleteBoxPruningTest(NUM_SAP_BOXES,1); - // gCollisionTests[TEST_BULLET_SAP_8192] = new BulletSAPCompleteBoxPruningTest(NUM_SAP_BOXES,1); + gCollisionTests[TEST_BULLET_SAP_8192] = new BulletSAPCompleteBoxPruningTest(NUM_SAP_BOXES,1); // gCollisionTests[TEST_BULLET_SAP_SORTEDPAIRS_8192] = new BulletSAPCompleteBoxPruningTest(NUM_SAP_BOXES,3); gCollisionTests[TEST_BULLET_MULTISAP_8192] = new BulletSAPCompleteBoxPruningTest(NUM_SAP_BOXES,6); // gCollisionTests[TEST_BIPARTITE_BOX_PRUNING] = new BipartiteBoxPruningTest; gCollisionTests[TEST_DBVT_8192] = new BulletSAPCompleteBoxPruningTest(NUM_SAP_BOXES,7); + gCollisionTests[TEST_OPCODE_ARRAY_SAP] = new OpcodeArraySAPTest(NUM_SAP_BOXES); for(int i=0;iInit(); diff --git a/Extras/CDTestFramework/CDTestFramework.vcproj b/Extras/CDTestFramework/CDTestFramework.vcproj index 588aadcf7..b0180087c 100644 --- a/Extras/CDTestFramework/CDTestFramework.vcproj +++ b/Extras/CDTestFramework/CDTestFramework.vcproj @@ -1,7 +1,7 @@ + + @@ -263,6 +272,14 @@ RelativePath=".\OBBMeshQuery.h" > + + + + @@ -303,6 +320,10 @@ /> + + @@ -317,14 +338,6 @@ Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > - - - - max.x) max.x = p.x; + if(p.x < min.x) min.x = p.x; + + if(p.y > max.y) max.y = p.y; + if(p.y < min.y) min.y = p.y; + + if(p.z > max.z) max.z = p.z; + if(p.z < min.z) min.z = p.z; + } // Forward declarations class Sphere; @@ -35,6 +30,7 @@ subject to the following restrictions: //! Declarations of type-independent methods (most of them implemented in the .cpp) #define AABB_COMMON_METHODS \ AABB& Add(const AABB& aabb); \ + AABB& Sub(const AABB& aabb); \ float MakeCube(AABB& cube) const; \ void MakeSphere(Sphere& sphere) const; \ const sbyte* ComputeOutline(const Point& local_eye, sdword& num) const; \ @@ -69,7 +65,7 @@ subject to the following restrictions: Point mMax; }; - class ICEMATHS_API AABB + class ICEMATHS_API AABB : public Allocateable { public: //! Constructor @@ -126,17 +122,8 @@ subject to the following restrictions: * \param p [in] the next point */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void Extend(const Point& p) - { - if(p.x > mMax.x) mMax.x = p.x; - if(p.x < mMin.x) mMin.x = p.x; + inline_ void Extend(const Point& p) { ComputeMinMax(p, mMin, mMax); } - if(p.y > mMax.y) mMax.y = p.y; - if(p.y < mMin.y) mMin.y = p.y; - - if(p.z > mMax.z) mMax.z = p.z; - if(p.z < mMin.z) mMin.z = p.z; - } // Data access //! Get min point of the box @@ -492,18 +479,7 @@ subject to the following restrictions: #endif - inline_ void ComputeMinMax(const Point& p, Point& min, Point& max) - { - if(p.x > max.x) max.x = p.x; - if(p.x < min.x) min.x = p.x; - - if(p.y > max.y) max.y = p.y; - if(p.y < min.y) min.y = p.y; - - if(p.z > max.z) max.z = p.z; - if(p.z < min.z) min.z = p.z; - } - + //! Computes the AABB around a set of vertices inline_ void ComputeAABB(AABB& aabb, const Point* list, udword nb_pts) { if(list) @@ -519,4 +495,20 @@ subject to the following restrictions: } } -#endif // __ICEAABB_H__ + //! Computes the AABB around a set of vertices after transform by a matrix + inline_ void ComputeAABB(AABB& aabb, const Point* list, udword nb_pts, const Matrix4x4& world) + { + if(list) + { + Point Maxi(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); + Point Mini(MAX_FLOAT, MAX_FLOAT, MAX_FLOAT); + while(nb_pts--) + { +// _prefetch(list+1); // off by one ? + ComputeMinMax((*list++)*world, Mini, Maxi); + } + aabb.SetMinMax(Mini, Maxi); + } + } + +#endif // ICEAABB_H diff --git a/Extras/CDTestFramework/Opcode/Ice/IceAllocator.cpp b/Extras/CDTestFramework/Opcode/Ice/IceAllocator.cpp new file mode 100644 index 000000000..9c661b64f --- /dev/null +++ b/Extras/CDTestFramework/Opcode/Ice/IceAllocator.cpp @@ -0,0 +1,805 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an allocator base class. + * \file IceAllocator.cpp + * \author Pierre Terdiman + * \date December, 19, 2003 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "StdAfx.h" +#include + +using namespace IceCore; + +//#define ZERO_OVERHEAD_RELEASE +#define NEW_CODE +// For some reason dmalloc seems a lot slower than the system malloc? +//#define USE_DMALLOC + +#ifdef USE_DMALLOC + #include "dmalloc.h" + #define LOCAL_MALLOC dlmalloc + #define LOCAL_FREE dlfree +#else + #define LOCAL_MALLOC ::malloc + #define LOCAL_FREE ::free +#endif + + +// WARNING: this makes allocations a lot slower. Only use when tracking memory leaks. +//#define ALLOC_STRINGS + +// ### doesn't seem that useful +//#define FAST_BUFFER_SIZE 256*1024 + +#define DEBUG_IDENTIFIER 0xBeefBabe +#define DEBUG_DEALLOCATED 0xDeadDead + +#ifdef ALLOC_STRINGS +static const char* AllocString(const char* str) +{ + if(!str) return null; + char* mem = (char*)LOCAL_MALLOC(strlen(str)+1); + strcpy(mem, str); + return mem; +} + +static void FreeString(const char* str) +{ + if(str) LOCAL_FREE((void*)str); +} + +#endif + + class DefaultAllocator : public Allocator + { + public: + DefaultAllocator(); + virtual ~DefaultAllocator(); + + void reset(); + + override(Allocator) void* malloc(size_t size, MemoryType type); + override(Allocator) void* mallocDebug(size_t size, const char* filename, udword line, const char* class_name, MemoryType type); + override(Allocator) void* realloc(void* memory, size_t size); + override(Allocator) void* shrink(void* memory, size_t size); + override(Allocator) void free(void* memory); + + void DumpCurrentMemoryState() const; + + void** mMemBlockList; + udword mMemBlockListSize; +#ifdef NEW_CODE + udword mFirstFree; +#else + udword mMemBlockFirstFree; +#endif + udword mMemBlockUsed; + + sdword mNbAllocatedBytes; + sdword mHighWaterMark; + sdword mTotalNbAllocs; + sdword mNbAllocs; + sdword mNbReallocs; +#ifdef FAST_BUFFER_SIZE + udword mNbFastBytes; + udword mFastBufferOffset; + ubyte* mFastBuffer; +#endif + }; + +#define MEMBLOCKSTART 64 + + struct DebugBlock + { + udword mCheckValue; +#ifdef FAST_BUFFER_SIZE + MemoryType mType; +#endif + udword mSize; + const char* mFilename; + udword mLine; + udword mSlotIndex; + const char* mClassName; + }; + +#ifndef FAST_BUFFER_SIZE + ICE_COMPILE_TIME_ASSERT(sizeof(DebugBlock)==24); // Prevents surprises..... +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +DefaultAllocator::DefaultAllocator() : mNbAllocatedBytes(0), mHighWaterMark(0), mTotalNbAllocs(0), mNbAllocs(0), mNbReallocs(0) +{ + mMemBlockList = null; + +#ifdef _DEBUG + // Initialize the Memory blocks list (DEBUG mode only) + mMemBlockList = (void**)LOCAL_MALLOC(MEMBLOCKSTART*sizeof(void*)); + ZeroMemory(mMemBlockList, MEMBLOCKSTART*sizeof(void*)); + mMemBlockListSize = MEMBLOCKSTART; +#ifdef NEW_CODE + mFirstFree = INVALID_ID; +#else + mMemBlockFirstFree = 0; +#endif + mMemBlockUsed = 0; +#endif + + +#ifdef FAST_BUFFER_SIZE + mNbFastBytes = 0; + mFastBufferOffset = 0; + mFastBuffer = (ubyte*)LOCAL_MALLOC(FAST_BUFFER_SIZE); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +DefaultAllocator::~DefaultAllocator() +{ +#ifdef FAST_BUFFER_SIZE + mNbFastBytes = 0; + mFastBufferOffset = 0; + if(mFastBuffer) LOCAL_FREE(mFastBuffer); + mFastBuffer = null; +#endif + +#ifdef _DEBUG + + // Ok, it is a bad idea to use _F() here, because it internally uses the allocator (for the log string). So let's use good old C style here. + char Buffer[4096]; + + if(mNbAllocatedBytes) + { + sprintf(Buffer, "Memory leak detected: %d bytes non released\n", mNbAllocatedBytes); +// IceTrace(Buffer); +// IceTrace(_F("Memory leak detected: %d bytes non released\n", mNbAllocatedBytes)); + } + if(mNbAllocs) + { + sprintf(Buffer, "Remaining allocs: %d\n", mNbAllocs); +// IceTrace(Buffer); +// IceTrace(_F("Remaining allocs: %d\n", mNbAllocs)); + } +// IceTrace(_F("Nb alloc: %d\n", mTotalNbAllocs)); + sprintf(Buffer, "Total nb alloc: %d\n", mTotalNbAllocs); +// IceTrace(Buffer); + +// IceTrace(_F("Nb realloc: %d\n", mNbReallocs)); + sprintf(Buffer, "Nb realloc: %d\n", mNbReallocs); +// IceTrace(Buffer); + +// IceTrace(_F("High water mark: %d Kb\n", mHighWaterMark/1024)); + sprintf(Buffer, "High water mark: %d Kb\n", mHighWaterMark/1024); +// IceTrace(Buffer); + + // Scanning for memory leaks + if(mMemBlockList && mNbAllocs) + { + udword NbLeaks = 0; +// IceTrace("\n\n ICE Message Memory leaks detected :\n\n"); + +#ifdef NEW_CODE + for(udword i=0; imSize, DB->mClassName, DB->mFilename, DB->mLine)); + sprintf(Buffer, " Address 0x%.8X, %d bytes (%s), allocated in: %s(%d):\n\n", DB+1, DB->mSize, DB->mClassName, DB->mFilename, DB->mLine); +// IceTrace(Buffer); + + NbLeaks++; +// Free(cur+4); + } +#else + for(udword i=0, j=0; imSize, DB->mClassName, DB->mFilename, DB->mLine)); + sprintf(Buffer, " Address 0x%.8X, %d bytes (%s), allocated in: %s(%d):\n\n", DB+1, DB->mSize, DB->mClassName, DB->mFilename, DB->mLine); + IceTrace(Buffer); + + NbLeaks++; +// Free(cur+4); + } +#endif +// IceTrace(_F("\n Dump complete (%d leaks)\n\n", NbLeaks)); + sprintf(Buffer, "\n Dump complete (%d leaks)\n\n", NbLeaks); +// IceTrace(Buffer); + } + // Free the Memory Block list + if(mMemBlockList) LOCAL_FREE(mMemBlockList); + mMemBlockList = null; +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void DefaultAllocator::reset() +{ + mNbAllocatedBytes = 0; + mHighWaterMark = 0; + mNbAllocs = 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void* DefaultAllocator::malloc(udword size, MemoryType type) +{ +// return ::malloc(size); + +#ifdef _DEBUG + return mallocDebug(size, null, 0, "Undefined", type); +#endif + + if(!size) + { +#ifdef _DEBUG +// IceTrace("Warning: trying to allocate 0 bytes\n"); +#endif + return null; + } + + mTotalNbAllocs++; + mNbAllocs++; + + mNbAllocatedBytes+=size; + if(mNbAllocatedBytes>mHighWaterMark) mHighWaterMark = mNbAllocatedBytes; + +#ifdef ZERO_OVERHEAD_RELEASE + return LOCAL_MALLOC(size); +#else + void* ptr = (void*)LOCAL_MALLOC(size+8); + udword* blockStart = (udword*)ptr; + blockStart[0] = DEBUG_IDENTIFIER; + blockStart[1] = size; + return ((udword*)ptr)+2; +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void* DefaultAllocator::mallocDebug(size_t size, const char* filename, udword line, const char* class_name, MemoryType type) +{ +#ifdef _DEBUG + if(!size) + { +// IceTrace("Warning: trying to allocate 0 bytes\n"); + return null; + } + + // Catch improper use of alloc macro... + if(0 && class_name) + { + const char* c = class_name; + while(*c) + { + if(*c==']' || *c=='[') + { + int stop=0; + } + c++; + } + } + + // Make sure size is even + if(size&1) size++; + +#ifdef FAST_BUFFER_SIZE + // Allocate one debug block in front of each real allocation + void* ptr = null; + if(type==MEMORY_TEMP) + { + udword NeededSize = size + sizeof(DebugBlock); + if(mFastBufferOffset + NeededSize <= FAST_BUFFER_SIZE) + { + ptr = mFastBuffer + mFastBufferOffset; + mFastBufferOffset += NeededSize; + mNbFastBytes += NeededSize; + } + } + + if(!ptr) + { + ptr = (void*)LOCAL_MALLOC(size + sizeof(DebugBlock)); + type = MEMORY_PERSISTENT; + } +#else + // Allocate one debug block in front of each real allocation + void* ptr = (void*)LOCAL_MALLOC(size + sizeof(DebugBlock)); +#endif + ASSERT(IS_ALIGNED_2(udword(ptr))); + + // Fill debug block + DebugBlock* DB = (DebugBlock*)ptr; + DB->mCheckValue = DEBUG_IDENTIFIER; +#ifdef FAST_BUFFER_SIZE + DB->mType = type; +#endif + DB->mSize = size; + DB->mLine = line; + DB->mSlotIndex = INVALID_ID; +#ifdef ALLOC_STRINGS + DB->mFilename = AllocString(filename); + DB->mClassName = AllocString(class_name); +#else + DB->mFilename = filename; + DB->mClassName = class_name; +#endif + + // Update global stats + mTotalNbAllocs++; + mNbAllocs++; + mNbAllocatedBytes += size; + if(mNbAllocatedBytes>mHighWaterMark) + mHighWaterMark = mNbAllocatedBytes; + + // Insert the allocated block in the debug memory block list + if(mMemBlockList) + { +#ifdef NEW_CODE + if(mFirstFree!=INVALID_ID) + { + // Recycle old location + + udword NextFree = udword(mMemBlockList[mFirstFree]); + if(NextFree!=INVALID_ID) NextFree>>=1; + + mMemBlockList[mFirstFree] = ptr; + DB->mSlotIndex = mFirstFree; + + mFirstFree = NextFree; + } + else + { + if(mMemBlockUsed==mMemBlockListSize) + { + // Allocate a bigger block + void** tps = (void**)LOCAL_MALLOC((mMemBlockListSize+MEMBLOCKSTART)*sizeof(void*)); + // Copy already used part + CopyMemory(tps, mMemBlockList, mMemBlockListSize*sizeof(void*)); + // Initialize remaining part + void* Next = tps + mMemBlockListSize; + ZeroMemory(Next, MEMBLOCKSTART*sizeof(void*)); + + // Free previous memory, setup new pointer + LOCAL_FREE(mMemBlockList); + mMemBlockList = tps; + // Setup new size + mMemBlockListSize += MEMBLOCKSTART; + } + + mMemBlockList[mMemBlockUsed] = ptr; + DB->mSlotIndex = mMemBlockUsed++; + } +#else + // Store allocated pointer in first free slot + mMemBlockList[mMemBlockFirstFree] = ptr; + DB->mSlotIndex = mMemBlockFirstFree; + + // Count number of used slots + mMemBlockUsed++; + + // Resize if needed + if(mMemBlockUsed==mMemBlockListSize) + { + // Allocate a bigger block + void** tps = (void**)LOCAL_MALLOC((mMemBlockListSize+MEMBLOCKSTART)*sizeof(void*)); + // Copy already used part + CopyMemory(tps, mMemBlockList, mMemBlockListSize*sizeof(void*)); + // Initialize remaining part + void* Next = tps + mMemBlockListSize; + ZeroMemory(Next, MEMBLOCKSTART*sizeof(void*)); + + // Free previous memory, setup new pointer + LOCAL_FREE(mMemBlockList); + mMemBlockList = tps; + // -1 because we'll do a ++ just afterwards + mMemBlockFirstFree = mMemBlockListSize-1; + // Setup new size + mMemBlockListSize += MEMBLOCKSTART; + } + + // Look for first free ### recode this ugly thing + while(mMemBlockList[++mMemBlockFirstFree] && (mMemBlockFirstFreemCheckValue!=DEBUG_IDENTIFIER) + { + // Not a valid memory block + return null; + } + if(size>DB->mSize) + { + // New size should be smaller! + return null; + } + + // Try to shrink the block + void* Reduced = _expand(SystemPointer, size + sizeof(DebugBlock)); + if(!Reduced) return null; + + if(Reduced!=SystemPointer) + { + // Should not be possible?! + } + + // Update stats + mNbAllocatedBytes -= DB->mSize; + mNbAllocatedBytes += size; + // Setup new size + DB->mSize = size; + + return memory; // The pointer should not have changed! +#else + // Release codepath + udword* SystemPointer = ((udword*)memory)-2; + if(SystemPointer[0]!=DEBUG_IDENTIFIER) + { + // Not a valid memory block + return null; + } + if(size>SystemPointer[1]) + { + // New size should be smaller! + return null; + } + + // Try to shrink the block + void* Reduced = _expand(SystemPointer, size+8); + if(!Reduced) return null; + + if(Reduced!=SystemPointer) + { + // Should not be possible?! + } + + // Update stats + mNbAllocatedBytes -= SystemPointer[1]; + mNbAllocatedBytes += size; + // Setup new size + SystemPointer[1] = size; + + return memory; // The pointer should not have changed! +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void* DefaultAllocator::realloc(void* memory, udword size) +{ +// return ::realloc(memory, size); + + ASSERT(0); + return null; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void DefaultAllocator::free(void* memory) +{ + if(!memory) + { +#ifdef _DEBUG +// IceTrace("Warning: trying to free null pointer\n"); +#endif + return; + } + +#ifdef _DEBUG + DebugBlock* DB = ((DebugBlock*)memory)-1; + +// DebugBlock TmpDB = *DB; // Keep a local copy to have readable data when ::free() fails! + + // Check we allocated it + if(DB->mCheckValue!=DEBUG_IDENTIFIER) + { +// IceTrace("Error: free unknown memory!!\n"); + // ### should we really continue?? + return; + } + + // Update global stats + mNbAllocatedBytes -= DB->mSize; + mNbAllocs--; + +#ifdef NEW_CODE + // Remove the block from the Memory block list + if(mMemBlockList) + { + udword FreeSlot = DB->mSlotIndex; + ASSERT(mMemBlockList[FreeSlot]==DB); + + udword NextFree = mFirstFree; + if(NextFree!=INVALID_ID) + { + NextFree<<=1; + NextFree|=1; + } + + mMemBlockList[FreeSlot] = (void*)NextFree; + mFirstFree = FreeSlot; + } +#else + udword MemBlockFirstFree = DB->mSlotIndex; // The slot we are in + udword Line = DB->mLine; + const char* File = DB->mFilename; + + // Remove the block from the Memory block list + if(mMemBlockList) + { + ASSERT(mMemBlockList[MemBlockFirstFree]==DB); + mMemBlockList[MemBlockFirstFree] = null; + mMemBlockUsed--; + } +#endif + +#ifdef ALLOC_STRINGS + FreeString(DB->mClassName); + FreeString(DB->mFilename); +#endif + +#ifdef FAST_BUFFER_SIZE + if(DB->mType==MEMORY_TEMP) + { + mNbFastBytes -= DB->mSize + sizeof(DebugBlock); + if(mNbFastBytes==0) + { + mFastBufferOffset = 0; + } + return; + } +#endif + + // ### should be useless since we'll release the memory just afterwards + DB->mCheckValue = DEBUG_DEALLOCATED; + DB->mSize = 0; + DB->mClassName = null; + DB->mFilename = null; + DB->mSlotIndex = INVALID_ID; + DB->mLine = INVALID_ID; + + LOCAL_FREE(DB); +#else + // Release codepath + #ifdef ZERO_OVERHEAD_RELEASE + +// mNbAllocatedBytes -= ptr[1]; // ### use _msize() ? + mNbAllocs--; + LOCAL_FREE(memory); + + #else + + udword* ptr = ((udword*)memory)-2; + if(ptr[0]!=DEBUG_IDENTIFIER) + { + #ifdef _DEBUG + IceTrace("Error: free unknown memory!!\n"); + #endif + } + mNbAllocatedBytes -= ptr[1]; + if(mNbAllocatedBytes<0) + { + #ifdef _DEBUG + IceTrace(_F("Oops (%d)\n", ptr[1])); + #endif + } + mNbAllocs--; + ptr[0]=DEBUG_DEALLOCATED; + ptr[1]=0; + LOCAL_FREE(ptr); + + #endif +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +inline_ bool ValidAddress(const void* addy) +{ +#ifdef NEW_CODE + return (addy && !(udword(addy)&1)); +#else + return addy!=null; +#endif +} + +void DefaultAllocator::DumpCurrentMemoryState() const +{ +#ifdef _DEBUG + // Scanning for memory leaks + if(mMemBlockList && mMemBlockUsed) + { +// IceTrace("\n\n----ALLOCATOR MEMORY DUMP:\n\n"); + + // We can't just use the "const char*" stored in the debug blocks because they're not guaranteed to + // be unique for similar strings. For example if a Container is allocated from two different DLLs, + // the "Container" character string will be duplicated (one per DLL). So we need to group similar + // strings together using the actual characters, not just the string address. We also have to do this + // without allocating any new memory, since it would add new entries to the memory debug structure. + // + // The good news is that we don't care about speed too much in this function, since it's not supposed + // to be called all the time. + + struct Local + { + struct TmpStruct + { + const char* mName; + udword mSize; + }; + static int SortCB(const void* elem1, const void* elem2) + { + const TmpStruct* s1 = (const TmpStruct*)elem1; + const TmpStruct* s2 = (const TmpStruct*)elem2; + return strcmp(s1->mName, s2->mName); + } + }; + + Local::TmpStruct* SortedStrings = (Local::TmpStruct*)LOCAL_MALLOC(sizeof(Local::TmpStruct)*mMemBlockListSize); + udword NbStrings = 0; + udword TotalSize = 0; + for(udword i=0;imClassName) + { + SortedStrings[NbStrings].mName = DB->mClassName; // Memory by class +// SortedStrings[NbStrings].mName = DB->mFilename; // Memory by file + SortedStrings[NbStrings].mSize = DB->mSize; + TotalSize += DB->mSize; + NbStrings++; + } + } + } + qsort(SortedStrings, NbStrings, sizeof(Local::TmpStruct), Local::SortCB); + + // Strings are now sorted. They might still be duplicated, i.e. we may have two strings for the same + // class. So now we parse the list and collect used memory for all classes. Then we sort this again, + // to report results in order of increasing memory. + + udword NbClasses=0; + udword* Classes = (udword*)LOCAL_MALLOC(sizeof(udword)*NbStrings); + udword* Sizes = (udword*)LOCAL_MALLOC(sizeof(udword)*NbStrings); + + udword CurrentSize = SortedStrings[0].mSize; + const char* CurrentClass = SortedStrings[0].mName; + for(udword i=1;i<=NbStrings;i++) // One more time on purpose + { + const char* Current = null; + if(i!=NbStrings) + { + Current = SortedStrings[i].mName; + } + + if(Current && strcmp(Current, CurrentClass)==0) + { + // Same class + CurrentSize += SortedStrings[i].mSize; + } + else + { + // New class + + // Store previous class + if(CurrentClass) + { + Classes[NbClasses] = (udword)CurrentClass; // We can store this pointer now because it's unique in our new array + Sizes[NbClasses++] = CurrentSize; + } + + // Next one + if(Current) + { + CurrentClass = Current; + CurrentSize = SortedStrings[i].mSize; + } + } + } + + udword* Ranks0 = (udword*)LOCAL_MALLOC(sizeof(udword)*NbClasses); + udword* Ranks1 = (udword*)LOCAL_MALLOC(sizeof(udword)*NbClasses); + + StackRadixSort(RS, Ranks0, Ranks1); + const udword* Sorted = RS.Sort(Sizes, NbClasses).GetRanks(); + for(udword i=0;iDumpCurrentMemoryState(); +} + +void InitDefaultAllocator() +{ +// gDefault = ::new DefaultAllocator; +} + +void ReleaseDefaultAllocator() +{ +// if(gDefault) ::delete gDefault; +// gDefault = null; +} diff --git a/Extras/CDTestFramework/Opcode/Ice/IceAllocator.h b/Extras/CDTestFramework/Opcode/Ice/IceAllocator.h new file mode 100644 index 000000000..ab6ce4134 --- /dev/null +++ b/Extras/CDTestFramework/Opcode/Ice/IceAllocator.h @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an allocator base class. + * \file IceAllocator.h + * \author Pierre Terdiman + * \date December, 19, 2003 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef ICEALLOCATOR_H +#define ICEALLOCATOR_H + + enum MemoryType + { + MEMORY_PERSISTENT, + MEMORY_TEMP, + }; + + class ICECORE_API Allocator + { + public: + virtual void* malloc(size_t size, MemoryType type) = 0; + virtual void* mallocDebug(size_t size, const char* filename, udword line, const char* class_name, MemoryType type) = 0; + virtual void* realloc(void* memory, size_t size) = 0; + virtual void* shrink(void* memory, size_t size) = 0; + virtual void free(void* memory) = 0; + }; + + FUNCTION ICECORE_API Allocator* GetAllocator(); + FUNCTION ICECORE_API bool SetAllocator(Allocator& allocator); + FUNCTION ICECORE_API void DumpMemory(); + + class ICECORE_API Allocateable + { + public: +#ifdef DONT_TRACK_MEMORY_LEAKS + inline_ void* operator new (size_t size, MemoryType type) { return GetAllocator()->malloc(size, type); } + inline_ void* operator new (size_t size, const char * filename, int line, const char* class_name, MemoryType type) { return GetAllocator()->mallocDebug(size, filename, line, class_name, type); } + inline_ void* operator new[] (size_t size, MemoryType type) { return GetAllocator()->malloc(size, type); } + inline_ void* operator new[] (size_t size, const char * filename, int line, const char* class_name, MemoryType type) { return GetAllocator()->mallocDebug(size, filename, line, class_name, type); } + inline_ void operator delete (void* p) { GetAllocator()->free(p); } + inline_ void operator delete (void* p, MemoryType) { GetAllocator()->free(p); } + inline_ void operator delete (void* p, const char*, int, const char*, MemoryType) { GetAllocator()->free(p); } + inline_ void operator delete[] (void* p) { GetAllocator()->free(p); } + inline_ void operator delete[] (void* p, MemoryType) { GetAllocator()->free(p); } + inline_ void operator delete[] (void* p, const char*, int, const char*, MemoryType) { GetAllocator()->free(p); } +#endif + }; + +#endif // ICEALLOCATOR_H diff --git a/Extras/CDTestFramework/Opcode/Ice/IceAssert.h b/Extras/CDTestFramework/Opcode/Ice/IceAssert.h new file mode 100644 index 000000000..c419c5873 --- /dev/null +++ b/Extras/CDTestFramework/Opcode/Ice/IceAssert.h @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains custom assertion code. + * \file IceAssert.h + * \author Pierre Terdiman + * \date January, 14, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef ICEASSERT_H +#define ICEASSERT_H + +// Leave the {} so that you can write this kind of things safely in release mode: +// if(condition) ASSERT() + +#ifndef ASSERT + #if defined( _DEBUG ) + FUNCTION ICECORE_API bool CustomAssertFunction(int, char*, int, char*, bool&); + + //! Custom ASSERT function. Various usages: + //! ASSERT(condition) + //! ASSERT(!"Not implemented") + //! ASSERT(condition && "error text") + #define ASSERT(exp) \ + { \ + static bool IgnoreAlways = false; \ + if(!IgnoreAlways) \ + { \ + if(CustomAssertFunction((int)(exp), #exp, __LINE__, __FILE__, IgnoreAlways)) \ + { \ + _asm { int 3 } \ + } \ + } \ + } + #else + #define ASSERT(exp) {} + #endif +#endif + +#ifndef assert + #define assert ASSERT +#endif + + #define ICE_COMPILE_TIME_ASSERT(exp) extern char ICE_Dummy[ (exp) ? 1 : -1 ] + +#endif // ICEASSERT_H diff --git a/Extras/CDTestFramework/Opcode/Ice/IceBitArray.cpp b/Extras/CDTestFramework/Opcode/Ice/IceBitArray.cpp new file mode 100644 index 000000000..0b4f6be7e --- /dev/null +++ b/Extras/CDTestFramework/Opcode/Ice/IceBitArray.cpp @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for bit arrays. + * \file IceBitArray.cpp + * \author Pierre Terdiman + * \date February, 5, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A simple array of bits stored as bytes. + * + * \class Container + * \author Pierre Terdiman + * \version 1.0 + * \date February, 5, 2000 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "StdAfx.h" + +using namespace IceCore; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BitArray::BitArray() : mSize(0), mBits(null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BitArray::BitArray(udword nb_bits) : mSize(0), mBits(null) +{ + Init(nb_bits); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BitArray::~BitArray() +{ + ICE_FREE(mBits); + mSize = 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes the bit array for a given number of entries + * \param nb_bits [in] max number of entries in the array + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool BitArray::Init(udword nb_bits) +{ + mSize = BitsToDwords(nb_bits); + // Get ram for n bits + ICE_FREE(mBits); + mBits = (udword*)ICE_ALLOC(sizeof(udword)*mSize); + // Set all bits to 0 + ClearAll(); + return true; +} diff --git a/Extras/CDTestFramework/Opcode/Ice/IceBitArray.h b/Extras/CDTestFramework/Opcode/Ice/IceBitArray.h new file mode 100644 index 000000000..518776e96 --- /dev/null +++ b/Extras/CDTestFramework/Opcode/Ice/IceBitArray.h @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for bit arrays. + * \file IceBitArray.h + * \author Pierre Terdiman + * \date February, 5, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef ICEBITARRAY_H +#define ICEBITARRAY_H + + inline_ udword BitsToBytes(udword nb_bits) + { + return (nb_bits>>3) + ((nb_bits&7) ? 1 : 0); + } + + inline_ udword BitsToDwords(udword nb_bits) + { + return (nb_bits>>5) + ((nb_bits&31) ? 1 : 0); + } + + // Use that one instead of an array of bools. Takes less ram, nearly as fast [no bounds checkings and so on]. + class ICECORE_API BitArray + { + public: + //! Constructor + BitArray(); + BitArray(udword nb_bits); + //! Destructor + ~BitArray(); + + bool Init(udword nb_bits); + + // Data management + inline_ void SetBit(udword bit_number) { mBits[bit_number>>5] |= 1<<(bit_number&31); } + inline_ void ClearBit(udword bit_number) { mBits[bit_number>>5] &= ~(1<<(bit_number&31)); } + inline_ void ToggleBit(udword bit_number) { mBits[bit_number>>5] ^= 1<<(bit_number&31); } + + inline_ void ClearAll() { ZeroMemory(mBits, mSize*4); } + inline_ void SetAll() { FillMemory(mBits, mSize*4, 0xff); } + + // Data access + inline_ BOOL IsSet(udword bit_number) const { return mBits[bit_number>>5] & (1<<(bit_number&31)); } + + inline_ const udword* GetBits() const { return mBits; } + inline_ udword GetSize() const { return mSize; } + + protected: + udword* mBits; //!< Array of bits + udword mSize; //!< Size of the array in dwords + }; + + // - We consider square symmetric N*N matrices + // - A N*N symmetric matrix has N(N+1)/2 elements + // - A boolean version needs N(N+1)/16 bytes + // N NbBits NbBytes + // 4 10 - + // 8 36 4.5 + // 16 136 17 <= the one we select + // 32 528 66 + static ubyte BitMasks[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; + static ubyte NegBitMasks[] = { 0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F }; + class ICECORE_API BoolSquareSymmetricMatrix16 + { + public: + inline_ udword Index(udword x, udword y) const { if(x>y) Swap(x,y); return x + (y ? ((y-1)*y)>>1 : 0); } + + inline_ void Set(udword x, udword y) { udword i = Index(x, y); mBits[i>>3] |= BitMasks[i&7]; } + inline_ void Clear(udword x, udword y) { udword i = Index(x, y); mBits[i>>3] &= NegBitMasks[i&7]; } + inline_ void Toggle(udword x, udword y) { udword i = Index(x, y); mBits[i>>3] ^= BitMasks[i&7]; } + inline_ bool IsSet(udword x, udword y) const { udword i = Index(x, y); return (mBits[i>>3] & BitMasks[i&7])!=0; } + + inline_ void ClearAll() { ZeroMemory(mBits, 17); } + inline_ void SetAll() { FillMemory(mBits, 17, 0xff); } + + ubyte mBits[17]; + }; + +#endif // ICEBITARRAY_H diff --git a/Extras/CDTestFramework/Opcode/Ice/IceContainer.cpp b/Extras/CDTestFramework/Opcode/Ice/IceContainer.cpp index 53c2b4465..4af392b2f 100644 --- a/Extras/CDTestFramework/Opcode/Ice/IceContainer.cpp +++ b/Extras/CDTestFramework/Opcode/Ice/IceContainer.cpp @@ -1,19 +1,3 @@ -/* - * ICE / OPCODE - Optimized Collision Detection - * http://www.codercorner.com/Opcode.htm - * - * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains a simple container class. @@ -38,7 +22,7 @@ subject to the following restrictions: /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Precompiled Header -#include "Stdafx.h" +#include "StdAfx.h" using namespace IceCore; @@ -46,6 +30,7 @@ using namespace IceCore; #ifdef CONTAINER_STATS udword Container::mNbContainers = 0; udword Container::mUsedRam = 0; +LinkedList Container::mContainers; #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -58,6 +43,7 @@ Container::Container() : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGr #ifdef CONTAINER_STATS mNbContainers++; mUsedRam+=sizeof(Container); + mContainers.AddElem(this); #endif } @@ -66,11 +52,13 @@ Container::Container() : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGr * Constructor. Also allocates a given number of entries. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Container::Container(udword size, float growth_factor) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(growth_factor) +Container::Container(udword size, float growth_factor) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null) { + SetGrowthFactor(growth_factor); #ifdef CONTAINER_STATS mNbContainers++; mUsedRam+=sizeof(Container); + mContainers.AddElem(this); #endif SetSize(size); } @@ -85,6 +73,7 @@ Container::Container(const Container& object) : mMaxNbEntries(0), mCurNbEntries( #ifdef CONTAINER_STATS mNbContainers++; mUsedRam+=sizeof(Container); + mContainers.AddElem(this); #endif *this = object; } @@ -100,9 +89,25 @@ Container::~Container() #ifdef CONTAINER_STATS mNbContainers--; mUsedRam-=GetUsedRam(); + mContainers.RemElem(this); #endif } +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes the container so that it uses an external memory buffer. The container doesn't own the memory, resizing is disabled. + * \param max_entries [in] max number of entries in the container + * \param entries [in] external memory buffer + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Container::InitSharedBuffers(udword max_entries, udword* entries) +{ + Empty(); // Make sure everything has been released + mMaxNbEntries = max_entries; + mEntries = entries; + mGrowthFactor = -1.0f; // Negative growth ==> resize is disabled +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Clears the container. All stored values are deleted, and it frees used ram. @@ -115,7 +120,7 @@ Container& Container::Empty() #ifdef CONTAINER_STATS mUsedRam-=mMaxNbEntries*sizeof(udword); #endif - DELETEARRAY(mEntries); + if(mGrowthFactor>=0.0f) ICE_FREE(mEntries); // Release memory if we own it mCurNbEntries = mMaxNbEntries = 0; return *this; } @@ -129,6 +134,13 @@ Container& Container::Empty() /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Container::Resize(udword needed) { + // Check growth is allowed + if(mGrowthFactor<=0.0f) + { + ASSERT(!"Invalid operation - trying to resize a static buffer!"); + return false; + } + #ifdef CONTAINER_STATS // Subtract previous amount of bytes mUsedRam-=mMaxNbEntries*sizeof(udword); @@ -139,7 +151,7 @@ bool Container::Resize(udword needed) if(mMaxNbEntriesshrink(mEntries, sizeof(udword)*mCurNbEntries)) + return false; + +#ifdef CONTAINER_STATS + // Subtract previous amount of bytes + mUsedRam-=mMaxNbEntries*sizeof(udword); +#endif + + // Get just enough entries + mMaxNbEntries = mCurNbEntries; + +#ifdef CONTAINER_STATS + // Add current amount of bytes + mUsedRam+=mMaxNbEntries*sizeof(udword); +#endif + + return true; +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Checks whether the container already contains a given value. @@ -353,6 +402,19 @@ udword Container::GetUsedRam() const return sizeof(Container) + mMaxNbEntries * sizeof(udword); } +float Container::GetGrowthFactor() const +{ + return mGrowthFactor; +} + +void Container::SetGrowthFactor(float growth) +{ + // Negative growths are reserved for internal usages + if(growth<0.0f) growth = 0.0f; + mGrowthFactor = growth; +} + +//! Operator for "Container A = Container B" void Container::operator=(const Container& object) { SetSize(object.GetNbEntries()); diff --git a/Extras/CDTestFramework/Opcode/Ice/IceContainer.h b/Extras/CDTestFramework/Opcode/Ice/IceContainer.h index b354741e5..f5dab1558 100644 --- a/Extras/CDTestFramework/Opcode/Ice/IceContainer.h +++ b/Extras/CDTestFramework/Opcode/Ice/IceContainer.h @@ -1,19 +1,3 @@ -/* - * ICE / OPCODE - Optimized Collision Detection - * http://www.codercorner.com/Opcode.htm - * - * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains a simple container class. @@ -25,10 +9,12 @@ subject to the following restrictions: /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Include Guard -#ifndef __ICECONTAINER_H__ -#define __ICECONTAINER_H__ +#ifndef ICECONTAINER_H +#define ICECONTAINER_H - #define CONTAINER_STATS +// #define CONTAINER_STATS // #### doesn't work with micro-threads! + + class LinkedList; enum FindMode { @@ -38,7 +24,10 @@ subject to the following restrictions: FIND_FORCE_DWORD = 0x7fffffff }; - class ICECORE_API Container + class ICECORE_API Container : public Allocateable +#ifdef CONTAINER_STATS + , public ListElem +#endif { public: // Constructor / Destructor @@ -47,6 +36,15 @@ subject to the following restrictions: Container(udword size, float growth_factor); ~Container(); // Management + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Initializes the container so that it uses an external memory buffer. The container doesn't own the memory, resizing is disabled. + * \param max_entries [in] max number of entries in the container + * \param entries [in] external memory buffer + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void InitSharedBuffers(udword max_entries, udword* entries); + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * A O(1) method to add a value in the container. The container is automatically resized if needed. @@ -71,14 +69,37 @@ subject to the following restrictions: } inline_ Container& Add(const udword* entries, udword nb) + { + if(entries && nb) + { + // Resize if needed + if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb); + + // Add new entry + CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(udword)); + mCurNbEntries += nb; + } + return *this; + } + + inline_ Container& Add(const Container& container) + { + return Add(container.GetEntries(), container.GetNbEntries()); + } + + inline_ udword* Reserve(udword nb) { // Resize if needed if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb); - // Add new entry - CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(udword)); - mCurNbEntries+=nb; - return *this; + // We expect the user to fill reserved memory with 'nb' udwords + udword* Reserved = &mEntries[mCurNbEntries]; + + // Meanwhile, we do as if it had been filled + mCurNbEntries += nb; + + // This is mainly used to avoid the copy when possible + return Reserved; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -145,7 +166,7 @@ subject to the following restrictions: if(mCurNbEntries) mCurNbEntries = 0; } - // HANDLE WITH CARE + // HANDLE WITH CARE - I hope you know what you're doing inline_ void ForceSize(udword size) { mCurNbEntries = size; @@ -168,6 +189,8 @@ subject to the following restrictions: /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Refit(); + bool Shrink(); + // Checks whether the container already contains a given value. bool Contains(udword entry, udword* location=null) const; // Deletes an entry - doesn't preserve insertion order. @@ -184,15 +207,16 @@ subject to the following restrictions: Container& FindPrev(udword& entry, FindMode find_mode=FIND_CLAMP); // Data access. inline_ udword GetNbEntries() const { return mCurNbEntries; } //!< Returns the current number of entries. - inline_ udword GetEntry(udword i) const { return mEntries[i]; } //!< Returns ith entry + inline_ udword GetMaxNbEntries() const { return mMaxNbEntries; } //!< Returns max number of entries before resizing. + inline_ udword GetEntry(udword i) const { return mEntries[i]; } //!< Returns ith entry. inline_ udword* GetEntries() const { return mEntries; } //!< Returns the list of entries. inline_ udword GetFirst() const { return mEntries[0]; } inline_ udword GetLast() const { return mEntries[mCurNbEntries-1]; } // Growth control - inline_ float GetGrowthFactor() const { return mGrowthFactor; } //!< Returns the growth factor - inline_ void SetGrowthFactor(float growth) { mGrowthFactor = growth; } //!< Sets the growth factor + float GetGrowthFactor() const; //!< Returns the growth factor + void SetGrowthFactor(float growth); //!< Sets the growth factor inline_ bool IsFull() const { return mCurNbEntries==mMaxNbEntries; } //!< Checks the container is full inline_ BOOL IsNotEmpty() const { return mCurNbEntries; } //!< Checks the container is empty @@ -208,12 +232,14 @@ subject to the following restrictions: void operator = (const Container& object); #ifdef CONTAINER_STATS - inline_ udword GetNbContainers() const { return mNbContainers; } - inline_ udword GetTotalBytes() const { return mUsedRam; } + static udword GetNbContainers() { return mNbContainers; } + static udword GetTotalBytes() { return mUsedRam; } + static LinkedList& GetContainers() { return mContainers; } private: static udword mNbContainers; //!< Number of containers around static udword mUsedRam; //!< Amount of bytes used by containers in the system + static LinkedList mContainers; #endif private: // Resizing @@ -225,4 +251,4 @@ subject to the following restrictions: float mGrowthFactor; //!< Resize: new number of entries = old number * mGrowthFactor }; -#endif // __ICECONTAINER_H__ +#endif // ICECONTAINER_H diff --git a/Extras/CDTestFramework/Opcode/Ice/IceFPU.h b/Extras/CDTestFramework/Opcode/Ice/IceFPU.h index 90119d9b8..bf5fa46ed 100644 --- a/Extras/CDTestFramework/Opcode/Ice/IceFPU.h +++ b/Extras/CDTestFramework/Opcode/Ice/IceFPU.h @@ -1,19 +1,3 @@ -/* - * ICE / OPCODE - Optimized Collision Detection - * http://www.codercorner.com/Opcode.htm - * - * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains FPU related code. @@ -25,8 +9,8 @@ subject to the following restrictions: /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Include Guard -#ifndef __ICEFPU_H__ -#define __ICEFPU_H__ +#ifndef ICEFPU_H +#define ICEFPU_H #define SIGN_BITMASK 0x80000000 @@ -46,6 +30,12 @@ subject to the following restrictions: //! Don't use it blindly, it can be faster or slower than the FPU comparison, depends on the context. #define IS_NEGATIVE_FLOAT(x) (IR(x)&0x80000000) + //! Checks 2 values have different signs + inline_ BOOL DifferentSign(float f0, float f1) + { + return (IR(f0)^IR(f1))&SIGN_BITMASK; + } + //! Fast fabs for floating-point values. It just clears the sign bit. //! Don't use it blindy, it can be faster or slower than the FPU comparison, depends on the context. inline_ float FastFabs(float x) @@ -292,6 +282,46 @@ subject to the following restrictions: return Res; } + //! A global function to find MAX(a,b,c,d) using FCOMI/FCMOV + inline_ float FCMax4(float a, float b, float c, float d) + { + float Res; + _asm fld [a] + _asm fld [b] + _asm fld [c] + _asm fld [d] + FCOMI_ST1 + FCMOVB_ST1 + FCOMI_ST2 + FCMOVB_ST2 + FCOMI_ST3 + FCMOVB_ST3 + _asm fstp [Res] + _asm fcompp + _asm fcomp + return Res; + } + + //! A global function to find MIN(a,b,c,d) using FCOMI/FCMOV + inline_ float FCMin4(float a, float b, float c, float d) + { + float Res; + _asm fld [a] + _asm fld [b] + _asm fld [c] + _asm fld [d] + FCOMI_ST1 + FCMOVNB_ST1 + FCOMI_ST2 + FCMOVNB_ST2 + FCOMI_ST3 + FCMOVNB_ST3 + _asm fstp [Res] + _asm fcompp + _asm fcomp + return Res; + } + inline_ int ConvertToSortable(float f) { int& Fi = (int&)f; @@ -302,6 +332,32 @@ subject to the following restrictions: return Fi; } + inline_ udword EncodeFloat(const float val) + { + // We may need to check on -0 and 0 + // But it should make no practical difference. + udword ir = IR(val); + + if(ir & 0x80000000) //negative? + ir = ~ir;//reverse sequence of negative numbers + else + ir |= 0x80000000; // flip sign + + return ir; + } + + inline_ float DecodeFloat(udword ir) + { + udword rv; + + if(ir & 0x80000000) //positive? + rv = ir & ~0x80000000; //flip sign + else + rv = ~ir; //undo reversal + + return FR(rv); + } + enum FPUMode { FPU_FLOOR = 0, @@ -330,4 +386,18 @@ subject to the following restrictions: FUNCTION ICECORE_API int intFloor(const float& f); FUNCTION ICECORE_API int intCeil(const float& f); -#endif // __ICEFPU_H__ + inline_ sdword MyFloor(float f) + { + return (sdword)f - (IR(f)>>31); + } + + class ICECORE_API FPUGuard + { + public: + FPUGuard(); + ~FPUGuard(); + private: + uword mControlWord; + }; + +#endif // ICEFPU_H diff --git a/Extras/CDTestFramework/Opcode/Ice/IceHashing.h b/Extras/CDTestFramework/Opcode/Ice/IceHashing.h new file mode 100644 index 000000000..4ac1fd53d --- /dev/null +++ b/Extras/CDTestFramework/Opcode/Ice/IceHashing.h @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains hashing code. + * \file IceHashing.h + * \author Pierre Terdiman + * \date May, 08, 1999 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef ICEHASHING_H +#define ICEHASHING_H + + #define HashSize(n) ((udword)1<<(n)) + #define HashMask(n) (HashSize(n)-1) + + ICECORE_API udword Hash(const char* str); + ICECORE_API udword Hash(ubyte* k, udword length, udword initval); + + // Bob Jenkin's hash + inline_ unsigned int Hash32Bits_0(unsigned int key) + { + key += (key << 12); + key ^= (key >> 22); + key += (key << 4); + key ^= (key >> 9); + key += (key << 10); + key ^= (key >> 2); + key += (key << 7); + key ^= (key >> 12); + return key; + } + + // Thomas Wang's hash + inline_ int Hash32Bits_1(int key) + { + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; + } + + // Thomas Wang's hash + inline_ __int64 Hash64Bits_0(__int64 key) + { + key += ~(key << 32); + key ^= (key >> 22); + key += ~(key << 13); + key ^= (key >> 8); + key += (key << 3); + key ^= (key >> 15); + key += ~(key << 27); + key ^= (key >> 31); + return key; + } + + inline_ __int64 Hash64Bits_1(__int64 key) + { + __int64 c1 = 0x6e5ea73858134343L; + __int64 c2 = 0xb34e8f99a2ec9ef5L; + key ^= ((c1 ^ key) >> 32); + key *= c1; + key ^= ((c2 ^ key) >> 31); + key *= c2; + key ^= ((c1 ^ key) >> 32); + return key; + } + + inline_ udword Hash(udword id0, udword id1) + { + return Hash32Bits_1( (id0&0xffff)|(id1<<16) ); + } + +#endif // ICEHASHING_H diff --git a/Extras/CDTestFramework/Opcode/Ice/IceMemoryMacros.h b/Extras/CDTestFramework/Opcode/Ice/IceMemoryMacros.h index d845e52fb..f31c59408 100644 --- a/Extras/CDTestFramework/Opcode/Ice/IceMemoryMacros.h +++ b/Extras/CDTestFramework/Opcode/Ice/IceMemoryMacros.h @@ -1,19 +1,3 @@ -/* - * ICE / OPCODE - Optimized Collision Detection - * http://www.codercorner.com/Opcode.htm - * - * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains all memory macros. @@ -25,8 +9,8 @@ subject to the following restrictions: /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Include Guard -#ifndef __ICEMEMORYMACROS_H__ -#define __ICEMEMORYMACROS_H__ +#ifndef ICEMEMORYMACROS_H +#define ICEMEMORYMACROS_H #undef ZeroMemory #undef CopyMemory @@ -40,7 +24,7 @@ subject to the following restrictions: //! \see StoreDwords //! \see CopyMemory //! \see MoveMemory - inline_ void ZeroMemory(void* addr, udword size) { memset(addr, 0, size); } + inline_ void ZeroMemory(void* addr, regsize size) { memset(addr, 0, size); } //! Fills a buffer with a given byte. //! \param addr [in] buffer address @@ -50,7 +34,7 @@ subject to the following restrictions: //! \see ZeroMemory //! \see CopyMemory //! \see MoveMemory - inline_ void FillMemory(void* dest, udword size, ubyte val) { memset(dest, val, size); } + inline_ void FillMemory(void* dest, regsize size, ubyte val) { memset(dest, val, size); } //! Fills a buffer with a given dword. //! \param addr [in] buffer address @@ -90,7 +74,7 @@ subject to the following restrictions: //! \see FillMemory //! \see StoreDwords //! \see MoveMemory - inline_ void CopyMemory(void* dest, const void* src, udword size) { memcpy(dest, src, size); } + inline_ void CopyMemory(void* dest, const void* src, regsize size) { memcpy(dest, src, size); } //! Moves a buffer. //! \param addr [in] destination buffer address @@ -100,22 +84,97 @@ subject to the following restrictions: //! \see FillMemory //! \see StoreDwords //! \see CopyMemory - inline_ void MoveMemory(void* dest, const void* src, udword size) { memmove(dest, src, size); } + inline_ void MoveMemory(void* dest, const void* src, regsize size) { memmove(dest, src, size); } - #define SIZEOFOBJECT sizeof(*this) //!< Gives the size of current object. Avoid some mistakes (e.g. "sizeof(this)"). - //#define CLEAROBJECT { memset(this, 0, SIZEOFOBJECT); } //!< Clears current object. Laziness is my business. HANDLE WITH CARE. - #define DELETESINGLE(x) if (x) { delete x; x = null; } //!< Deletes an instance of a class. - #define DELETEARRAY(x) if (x) { delete []x; x = null; } //!< Deletes an array. - #define SAFE_RELEASE(x) if (x) { (x)->Release(); (x) = null; } //!< Safe D3D-style release - #define SAFE_DESTRUCT(x) if (x) { (x)->SelfDestruct(); (x) = null; } //!< Safe ICE-style release + //! Flexible buffer copy + //! \param src [in] source buffer address + //! \param dst [in] destination buffer address + //! \param nb_elem [in] number of elements to copy + //! \param elem_size [in] size of an element + //! \param stride [in] stride in bytes, including size of element + inline_ void FlexiCopy(const void* src, void* dst, udword nb_elem, regsize elem_size, udword stride) + { + ubyte* d = (ubyte*)dst; + const ubyte* s = (const ubyte*)src; + const ubyte* Last = s + stride*nb_elem; + while(s!=Last) + { + CopyMemory(d, s, elem_size); + d += elem_size; + s += stride; + } + } -#ifdef __ICEERROR_H__ - #define CHECKALLOC(x) if(!x) return SetIceError("Out of memory.", EC_OUT_OF_MEMORY); //!< Standard alloc checking. HANDLE WITH CARE. + //! Gives the size of current object. This avoids some mistakes (e.g. "sizeof(this)"). + #define SIZEOFOBJECT sizeof(*this) + + //! Clears current object. Laziness is my business! HANDLE WITH CARE. ### Removed, too dangerous, cleared too many v-tables + //#define CLEAROBJECT { memset(this, 0, SIZEOFOBJECT); } + + // The two macros below are here for several reasons: + // - sometimes we write "delete x" instead of "delete []x" just because we don't pay attention. Using the macro forces you + // to think about what you're deleting, just because you have to write the macro's name (SINGLE or ARRAY). + // - always clearing the pointer afterwards prevents some double-deletion in various situations. + // - deleting null is a valid operation according to the standard, yet some lame memory managers don't like it. In sake of + // robustness, we avoid trying. + + //! Deletes an instance of a class. + #define DELETESINGLE(x) if (x) { delete x; x = null; } + //! Deletes an array. + #define DELETEARRAY(x) if (x) { delete []x; x = null; } + + //! Safe D3D-style release + #define SAFE_RELEASE(x) if (x) { (x)->Release(); (x) = null; } + + //! Safe ICE-style release + #define SAFE_DESTRUCT(x) if (x) { (x)->SelfDestruct(); (x) = null; } + +#ifdef ICEERROR_H + //! Standard alloc checking. HANDLE WITH CARE. Relies on strict coding rules. Probably shouldn't be used outside of ICE. + #define CHECKALLOC(x) if(!x) return SetIceError("Out of memory.", EC_OUT_OF_MEMORY); #else #define CHECKALLOC(x) if(!x) return false; #endif //! Standard allocation cycle - #define SAFE_ALLOC(ptr, type, count) DELETEARRAY(ptr); ptr = new type[count]; CHECKALLOC(ptr); + #define SAFE_ALLOC(ptr, type, count) DELETEARRAY(ptr); ptr = new type[count]; CHECKALLOC(ptr); + #define SAFE_ICE_ALLOC(ptr, type, count) DELETEARRAY(ptr); ptr = ICE_NEW(type)[count]; CHECKALLOC(ptr); -#endif // __ICEMEMORYMACROS_H__ + //! Don't use inline for alloca !!! +#ifdef WIN32 + #define StackAlloc(x) _alloca(x) +#elif LINUX + #define StackAlloc(x) alloca(x) +#elif defined(__APPLE__) + #define StackAlloc(x) alloca(x) +#elif defined(_XBOX) + #define StackAlloc(x) _alloca(x) +#endif + +#ifdef _DEBUG +// #define ICE_ALLOC_TMP(x) GetAllocator()->mallocDebug(x, __FILE__, __LINE__, #x, MEMORY_TEMP) +// #define ICE_ALLOC(x) GetAllocator()->mallocDebug(x, __FILE__, __LINE__, #x, MEMORY_PERSISTENT) + #define ICE_ALLOC_TMP(x) GetAllocator()->mallocDebug(x, __FILE__, __LINE__, "(undefined)", MEMORY_TEMP) + #define ICE_ALLOC(x) GetAllocator()->mallocDebug(x, __FILE__, __LINE__, "(undefined)", MEMORY_PERSISTENT) + #define ICE_ALLOC_TMP2(x, y) GetAllocator()->mallocDebug(x, __FILE__, __LINE__, #y, MEMORY_TEMP) + #define ICE_ALLOC2(x, y) GetAllocator()->mallocDebug(x, __FILE__, __LINE__, #y, MEMORY_PERSISTENT) +#else + #define ICE_ALLOC_TMP(x) GetAllocator()->malloc(x, MEMORY_TEMP) + #define ICE_ALLOC(x) GetAllocator()->malloc(x, MEMORY_PERSISTENT) +#endif + #define ICE_FREE(x) if(x) { GetAllocator()->free(x); x = null; } + +#ifdef DONT_TRACK_MEMORY_LEAKS + #ifdef _DEBUG + #define ICE_NEW_TMP(x) new(__FILE__, __LINE__, #x, MEMORY_TEMP) x + #define ICE_NEW(x) new(__FILE__, __LINE__, #x, MEMORY_PERSISTENT) x + #else + #define ICE_NEW_TMP(x) new(MEMORY_TEMP) x + #define ICE_NEW(x) new(MEMORY_PERSISTENT) x + #endif +#else + #define ICE_NEW_TMP(x) new x + #define ICE_NEW(x) new x +#endif + +#endif // ICEMEMORYMACROS_H diff --git a/Extras/CDTestFramework/Opcode/Ice/IcePreprocessor.h b/Extras/CDTestFramework/Opcode/Ice/IcePreprocessor.h index e3a2d1173..9036feb85 100644 --- a/Extras/CDTestFramework/Opcode/Ice/IcePreprocessor.h +++ b/Extras/CDTestFramework/Opcode/Ice/IcePreprocessor.h @@ -1,19 +1,3 @@ -/* - * ICE / OPCODE - Optimized Collision Detection - * http://www.codercorner.com/Opcode.htm - * - * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains preprocessor stuff. This should be the first included header. @@ -25,8 +9,8 @@ subject to the following restrictions: /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Include Guard -#ifndef __ICEPREPROCESSOR_H__ -#define __ICEPREPROCESSOR_H__ +#ifndef ICEPREPROCESSOR_H +#define ICEPREPROCESSOR_H // Check platform #if defined( _WIN32 ) || defined( WIN32 ) @@ -40,6 +24,14 @@ subject to the following restrictions: #if defined(_MSC_VER) #pragma message("Compiling with VC++...") #define COMPILER_VISUAL_CPP + + #if _MSC_VER > 1300 + #pragma message("Compiling with VC7") + #define COMPILER_VC7 + #else + #pragma message("Compiling with VC6") + #define COMPILER_VC6 + #endif #else #pragma message("Compiling with unknown compiler...") #endif @@ -73,7 +65,7 @@ subject to the following restrictions: #endif #ifdef _DEBUG - // Here you may define items for debug builds + // Here you may define items for debug builds #endif #ifndef THIS_FILE @@ -82,18 +74,14 @@ subject to the following restrictions: #ifndef ICE_NO_DLL #ifdef ICECORE_EXPORTS - #define ICECORE_API __declspec(dllexport) + #define ICECORE_API __declspec(dllexport) #else - #define ICECORE_API __declspec(dllimport) + #define ICECORE_API __declspec(dllimport) #endif #else #define ICECORE_API #endif - // Don't override new/delete -// #define DEFAULT_NEWDELETE - #define DONT_TRACK_MEMORY_LEAKS - #define FUNCTION extern "C" // Cosmetic stuff [mainly useful with multiple inheritance] @@ -106,16 +94,16 @@ subject to the following restrictions: // #define inline_ inline // Contributed by Bruce Mitchener - #if defined(COMPILER_VISUAL_CPP) - #define inline_ __forceinline -// #define inline_ inline - #elif defined(__GNUC__) && __GNUC__ < 3 - #define inline_ inline - #elif defined(__GNUC__) - #define inline_ inline __attribute__ ((always_inline)) - #else - #define inline_ inline - #endif + #if defined(COMPILER_VISUAL_CPP) + #define inline_ __forceinline +// #define inline_ inline + #elif defined(__GNUC__) && __GNUC__ < 3 + #define inline_ inline + #elif defined(__GNUC__) + #define inline_ inline __attribute__ ((always_inline)) + #else + #define inline_ inline + #endif // Down the hatch #pragma inline_depth( 255 ) @@ -135,10 +123,36 @@ subject to the following restrictions: // ANSI compliance #ifdef _DEBUG // Remove painful warning in debug - inline_ bool __False__(){ return false; } - #define for if(__False__()){} else for + inline_ bool ReturnsFalse(){ return false; } + #define for if(ReturnsFalse()){} else for #else #define for if(0){} else for #endif -#endif // __ICEPREPROCESSOR_H__ + // Don't override new/delete + #define DEFAULT_NEWDELETE + #define DONT_TRACK_MEMORY_LEAKS + + //! Macro used to give me a clue when it crashes in release and only the assembly is available + #define INCLUDE_GUARDIANS + #ifdef INCLUDE_GUARDIANS + #define GUARD(x) \ + { \ + static const char guard_text[] = x; \ + _asm push eax \ + _asm nop \ + _asm nop \ + _asm nop \ + _asm nop \ + _asm lea eax, guard_text \ + _asm nop \ + _asm nop \ + _asm nop \ + _asm nop \ + _asm pop eax \ + } + #else + #define GUARD(x) + #endif + +#endif // ICEPREPROCESSOR_H diff --git a/Extras/CDTestFramework/Opcode/Ice/IceRevisitedRadix.cpp b/Extras/CDTestFramework/Opcode/Ice/IceRevisitedRadix.cpp index 3a77b4a22..85c62127b 100644 --- a/Extras/CDTestFramework/Opcode/Ice/IceRevisitedRadix.cpp +++ b/Extras/CDTestFramework/Opcode/Ice/IceRevisitedRadix.cpp @@ -1,19 +1,3 @@ -/* - * ICE / OPCODE - Optimized Collision Detection - * http://www.codercorner.com/Opcode.htm - * - * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains source code from the article "Radix Sort Revisited". @@ -44,12 +28,18 @@ subject to the following restrictions: * - 01.20.02: bugfix! In very particular cases the last pass was skipped in the float code-path, leading to incorrect sorting...... * - 01.02.02: - "mIndices" renamed => "mRanks". That's a rank sorter after all. * - ranks are not "reset" anymore, but implicit on first calls - * - 07.05.02: - offsets rewritten with one less indirection. - * - 11.03.02: - "bool" replaced with RadixHint enum + * - 07.05.02: offsets rewritten with one less indirection. + * - 11.03.02: "bool" replaced with RadixHint enum + * - 07.15.04: stack-based radix added + * - we want to use the radix sort but without making it static, and without allocating anything. + * - we internally allocate two arrays of ranks. Each of them has N udwords to sort N values. + * - 1Mb/2/sizeof(udword) = 131072 values max, at the same time. + * - 09.22.04: - adapted to MacOS by Chris Lamb + * - 01.12.06: - added optimizations suggested by Kyle Hubert * * \class RadixSort * \author Pierre Terdiman - * \version 1.4 + * \version 1.5 * \date August, 15, 1998 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -58,14 +48,13 @@ subject to the following restrictions: To do: - add an offset parameter between two input values (avoid some data recopy sometimes) - unroll ? asm ? - - 11 bits trick & 3 passes as Michael did - prefetch stuff the day I have a P3 - make a version with 16-bits indices ? */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Precompiled Header -#include "Stdafx.h" +#include "StdAfx.h" using namespace IceCore; @@ -82,17 +71,31 @@ using namespace IceCore; mPreviousSize = n; \ } +#if defined(__APPLE__) || defined(_XBOX) + #define H0_OFFSET 768 + #define H1_OFFSET 512 + #define H2_OFFSET 256 + #define H3_OFFSET 0 + #define BYTES_INC (3-j) +#else + #define H0_OFFSET 0 + #define H1_OFFSET 256 + #define H2_OFFSET 512 + #define H3_OFFSET 768 + #define BYTES_INC j +#endif + #define CREATE_HISTOGRAMS(type, buffer) \ /* Clear counters/histograms */ \ ZeroMemory(mHistogram, 256*4*sizeof(udword)); \ \ /* Prepare to count */ \ - ubyte* p = (ubyte*)input; \ - ubyte* pe = &p[nb*4]; \ - udword* h0= &mHistogram[0]; /* Histogram for first pass (LSB) */ \ - udword* h1= &mHistogram[256]; /* Histogram for second pass */ \ - udword* h2= &mHistogram[512]; /* Histogram for third pass */ \ - udword* h3= &mHistogram[768]; /* Histogram for last pass (MSB) */ \ + const ubyte* p = (const ubyte*)input; \ + const ubyte* pe = &p[nb*4]; \ + udword* h0= &mHistogram[H0_OFFSET]; /* Histogram for first pass (LSB) */ \ + udword* h1= &mHistogram[H1_OFFSET]; /* Histogram for second pass */ \ + udword* h2= &mHistogram[H2_OFFSET]; /* Histogram for third pass */ \ + udword* h3= &mHistogram[H3_OFFSET]; /* Histogram for last pass (MSB) */ \ \ bool AlreadySorted = true; /* Optimism... */ \ \ @@ -128,7 +131,7 @@ using namespace IceCore; else \ { \ /* Prepare for temporal coherence */ \ - udword* Indices = mRanks; \ + const udword* Indices = mRanks; \ type PrevVal = (type)buffer[*Indices]; \ \ while(p!=pe) \ @@ -159,7 +162,7 @@ using namespace IceCore; #define CHECK_PASS_VALIDITY(pass) \ /* Shortcut to current counters */ \ - udword* CurCount = &mHistogram[pass<<8]; \ + const udword* CurCount = &mHistogram[pass<<8]; \ \ /* Reset flag. The sorting pass is supposed to be performed. (default) */ \ bool PerformPass = true; \ @@ -183,12 +186,12 @@ using namespace IceCore; * Constructor. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -RadixSort::RadixSort() : mRanks(null), mRanks2(null), mCurrentSize(0), mTotalCalls(0), mNbHits(0) +RadixSort::RadixSort() : mRanks(null), mRanks2(null), mCurrentSize(0), mTotalCalls(0), mNbHits(0), mDeleteRanks(true) { #ifndef RADIX_LOCAL_RAM // Allocate input-independent ram - mHistogram = new udword[256*4]; - mOffset = new udword[256]; + mHistogram = ICE_ALLOC(sizeof(udword)*256*4); + mOffset = ICE_ALLOC(sizeof(udword)*256); #endif // Initialize indices INVALIDATE_RANKS; @@ -203,11 +206,14 @@ RadixSort::~RadixSort() { // Release everything #ifndef RADIX_LOCAL_RAM - DELETEARRAY(mOffset); - DELETEARRAY(mHistogram); + ICE_FREE(mOffset); + ICE_FREE(mHistogram); #endif - DELETEARRAY(mRanks2); - DELETEARRAY(mRanks); + if(mDeleteRanks) + { + ICE_FREE(mRanks2); + ICE_FREE(mRanks); + } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -219,14 +225,16 @@ RadixSort::~RadixSort() /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool RadixSort::Resize(udword nb) { - // Free previously used ram - DELETEARRAY(mRanks2); - DELETEARRAY(mRanks); - - // Get some fresh one - mRanks = new udword[nb]; CHECKALLOC(mRanks); - mRanks2 = new udword[nb]; CHECKALLOC(mRanks2); + if(mDeleteRanks) + { + // Free previously used ram + ICE_FREE(mRanks2); + ICE_FREE(mRanks); + // Get some fresh one + mRanks = (udword*)ICE_ALLOC(sizeof(udword)*nb); CHECKALLOC(mRanks); + mRanks2 = (udword*)ICE_ALLOC(sizeof(udword)*nb); CHECKALLOC(mRanks2); + } return true; } @@ -276,7 +284,7 @@ RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint) // have 2 code paths even if just a single opcode changes. Self-modifying code, someone? if(hint==RADIX_UNSIGNED) { CREATE_HISTOGRAMS(udword, input); } else { CREATE_HISTOGRAMS(sdword, input); } - +/* // Compute #negative values involved if needed udword NbNegativeValues = 0; if(hint==RADIX_SIGNED) @@ -287,7 +295,7 @@ RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint) udword* h3= &mHistogram[768]; for(udword i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part } - +*/ // Radix sort, j is the pass number (0=LSB, 3=MSB) for(udword j=0;j<4;j++) { @@ -311,23 +319,38 @@ RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint) else { // This is a special case to correctly handle negative integers. They're sorted in the right order but at the wrong place. - +/* // Create biased offsets, in order for negative numbers to be sorted as well // mOffset[0] = NbNegativeValues; // First positive number takes place after the negative ones - mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones // for(udword i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers + mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers // Fixing the wrong place for negative values // mOffset[128] = 0; - mLink[128] = mRanks2; // for(i=129;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; + mLink[128] = mRanks2; for(udword i=129;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; +*/ + +// From Kyle Hubert: + +//mOffset[128] = 0; +mLink[128] = mRanks2; +//for(i=129;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; +for(udword i=129;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; + +//mOffset[0] = mOffset[255] + CurCount[255]; +mLink[0] = mLink[255] + CurCount[255]; +//for(i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; +for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; + + } // Perform Radix Sort - ubyte* InputBytes = (ubyte*)input; - InputBytes += j; + const ubyte* InputBytes = (const ubyte*)input; + InputBytes += BYTES_INC; if(INVALID_RANKS) { // for(udword i=0;i127;i--) mOffset[i] = mOffset[i+1] + CurCount[i]; +for(udword i=254;i>127;i--) mLink[i] = mLink[i+1] + CurCount[i]; +//mOffset[0] = mOffset[128] + CurCount[128]; +mLink[0] = mLink[128] + CurCount[128]; +//for(udword i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; +for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; // Perform Radix Sort if(INVALID_RANKS) @@ -491,7 +532,9 @@ RadixSort& RadixSort::Sort(const float* input2, udword nb) } } // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap. - udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp; + udword* Tmp = mRanks; + mRanks = mRanks2; + mRanks2 = Tmp; } else { @@ -510,7 +553,9 @@ RadixSort& RadixSort::Sort(const float* input2, udword nb) } // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap. - udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp; + udword* Tmp = mRanks; + mRanks = mRanks2; + mRanks2 = Tmp; } } } @@ -534,3 +579,14 @@ udword RadixSort::GetUsedRam() const UsedRam += 2*CURRENT_SIZE*sizeof(udword); // 2 lists of indices return UsedRam; } + +bool RadixSort::SetRankBuffers(udword* ranks0, udword* ranks1) +{ + if(!ranks0 || !ranks1) return false; + + mRanks = ranks0; + mRanks2 = ranks1; + mDeleteRanks = false; + + return true; +} diff --git a/Extras/CDTestFramework/Opcode/Ice/IceRevisitedRadix.h b/Extras/CDTestFramework/Opcode/Ice/IceRevisitedRadix.h index 41bef43ee..99c62b512 100644 --- a/Extras/CDTestFramework/Opcode/Ice/IceRevisitedRadix.h +++ b/Extras/CDTestFramework/Opcode/Ice/IceRevisitedRadix.h @@ -1,19 +1,3 @@ -/* - * ICE / OPCODE - Optimized Collision Detection - * http://www.codercorner.com/Opcode.htm - * - * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains source code from the article "Radix Sort Revisited". @@ -25,8 +9,8 @@ subject to the following restrictions: /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Include Guard -#ifndef __ICERADIXSORT_H__ -#define __ICERADIXSORT_H__ +#ifndef ICERADIXSORT_H +#define ICERADIXSORT_H //! Allocate histograms & offsets locally #define RADIX_LOCAL_RAM @@ -39,7 +23,7 @@ subject to the following restrictions: RADIX_FORCE_DWORD = 0x7fffffff }; - class ICECORE_API RadixSort + class ICECORE_API RadixSort : public Allocateable { public: // Constructor/Destructor @@ -62,6 +46,9 @@ subject to the following restrictions: //! Returns the number of eraly exits due to temporal coherence. inline_ udword GetNbHits() const { return mNbHits; } + bool SetRankBuffers(udword* ranks0, udword* ranks1); + + PREVENT_COPY(RadixSort) private: #ifndef RADIX_LOCAL_RAM udword* mHistogram; //!< Counters for each byte @@ -73,9 +60,15 @@ subject to the following restrictions: // Stats udword mTotalCalls; //!< Total number of calls to the sort routine udword mNbHits; //!< Number of early exits due to coherence + // Stack-radix + bool mDeleteRanks; //!< // Internal methods void CheckResize(udword nb); bool Resize(udword nb); }; -#endif // __ICERADIXSORT_H__ + #define StackRadixSort(name, ranks0, ranks1) \ + RadixSort name; \ + name.SetRankBuffers(ranks0, ranks1); + +#endif // ICERADIXSORT_H diff --git a/Extras/CDTestFramework/Opcode/Ice/IceTypes.h b/Extras/CDTestFramework/Opcode/Ice/IceTypes.h index ad9348b8b..e4f9717ad 100644 --- a/Extras/CDTestFramework/Opcode/Ice/IceTypes.h +++ b/Extras/CDTestFramework/Opcode/Ice/IceTypes.h @@ -1,19 +1,3 @@ -/* - * ICE / OPCODE - Optimized Collision Detection - * http://www.codercorner.com/Opcode.htm - * - * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains custom types. @@ -25,13 +9,15 @@ subject to the following restrictions: /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Include Guard -#ifndef __ICETYPES_H__ -#define __ICETYPES_H__ +#ifndef ICETYPES_H +#define ICETYPES_H #define USE_HANDLE_MANAGER // Constants +#ifndef PI #define PI 3.1415926535897932384626433832795028841971693993751f //!< PI +#endif #define HALFPI 1.57079632679489661923f //!< 0.5 * PI #define TWOPI 6.28318530717958647692f //!< 2.0 * PI #define INVPI 0.31830988618379067154f //!< 1.0 / PI @@ -59,16 +45,32 @@ subject to the following restrictions: #define null 0 //!< our own NULL pointer // Custom types used in ICE - typedef signed char sbyte; //!< sizeof(sbyte) must be 1 - typedef unsigned char ubyte; //!< sizeof(ubyte) must be 1 - typedef signed short sword; //!< sizeof(sword) must be 2 - typedef unsigned short uword; //!< sizeof(uword) must be 2 - typedef signed int sdword; //!< sizeof(sdword) must be 4 - typedef unsigned int udword; //!< sizeof(udword) must be 4 - typedef signed __int64 sqword; //!< sizeof(sqword) must be 8 - typedef unsigned __int64 uqword; //!< sizeof(uqword) must be 8 + typedef signed char sbyte; //!< sizeof(sbyte) must be 1 + typedef unsigned char ubyte; //!< sizeof(ubyte) must be 1 + typedef signed short sword; //!< sizeof(sword) must be 2 + typedef unsigned short uword; //!< sizeof(uword) must be 2 + typedef signed int sdword; //!< sizeof(sdword) must be 4 + typedef unsigned int udword; //!< sizeof(udword) must be 4 +#ifdef WIN32 + typedef signed __int64 sqword; //!< sizeof(sqword) must be 8 + typedef unsigned __int64 uqword; //!< sizeof(uqword) must be 8 +#elif LINUX + typedef signed long long sqword; //!< sizeof(sqword) must be 8 + typedef unsigned long long uqword; //!< sizeof(uqword) must be 8 +#elif defined(__APPLE__) + typedef signed long long sqword; //!< sizeof(sqword) must be 8 + typedef unsigned long long uqword; //!< sizeof(uqword) must be 8 +#elif defined(_XBOX) + typedef signed __int64 sqword; //!< sizeof(sqword) must be 8 + typedef unsigned __int64 uqword; //!< sizeof(uqword) must be 8 +#endif typedef float float32; //!< sizeof(float32) must be 4 - typedef double float64; //!< sizeof(float64) must be 4 + typedef double float64; //!< sizeof(float64) must be 8 + typedef size_t regsize; //!< sizeof(regsize) must be sizeof(void*) + + // For test purpose you can force one of those: +// typedef udword regsize; +// typedef uqword regsize; ICE_COMPILE_TIME_ASSERT(sizeof(bool)==1); // ...otherwise things might fail with VC++ 4.2 ! ICE_COMPILE_TIME_ASSERT(sizeof(ubyte)==1); @@ -79,6 +81,9 @@ subject to the following restrictions: ICE_COMPILE_TIME_ASSERT(sizeof(sdword)==4); ICE_COMPILE_TIME_ASSERT(sizeof(uqword)==8); ICE_COMPILE_TIME_ASSERT(sizeof(sqword)==8); + ICE_COMPILE_TIME_ASSERT(sizeof(float32)==4); + ICE_COMPILE_TIME_ASSERT(sizeof(float64)==8); + ICE_COMPILE_TIME_ASSERT(sizeof(regsize)==sizeof(void*)); //! TO BE DOCUMENTED #define DECLARE_ICE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name @@ -90,7 +95,6 @@ subject to the following restrictions: #else typedef uword KID; //!< Kernel ID #endif - typedef udword RTYPE; //!< Relationship-type (!) between owners and references #define INVALID_ID 0xffffffff //!< Invalid dword ID (counterpart of null pointers) #ifdef USE_HANDLE_MANAGER #define INVALID_KID 0xffffffff //!< Invalid Kernel ID @@ -139,9 +143,7 @@ subject to the following restrictions: #define ONE_OVER_RAND_MAX (1.0f / float(RAND_MAX)) //!< Inverse of the max possible value returned by rand() - typedef int (__stdcall* PROC)(); //!< A standard procedure call. - typedef bool (*ENUMERATION)(udword value, udword param, udword context); //!< ICE standard enumeration call - typedef void** VTABLE; //!< A V-Table. + typedef void** VTABLE; //!< A V-Table. #undef MIN #undef MAX @@ -154,6 +156,7 @@ subject to the following restrictions: template inline_ void TSetMin (T& a, const T& b) { if(a>b) a = b; } template inline_ void TSetMax (T& a, const T& b) { if(a> 4) & 0x0f0f0f0f) | ((n << 4) & 0xf0f0f0f0); n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00); n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000); - // Etc for larger intergers (64 bits in Java) + // Etc for larger integers (64 bits in Java) // NOTE: the >> operation must be unsigned! (>>> in java) } @@ -48,15 +32,15 @@ subject to the following restrictions: inline_ udword CountBits(udword n) { // This relies of the fact that the count of n bits can NOT overflow - // an n bit interger. EG: 1 bit count takes a 1 bit interger, 2 bit counts - // 2 bit interger, 3 bit count requires only a 2 bit interger. + // an n bit integer. EG: 1 bit count takes a 1 bit integer, 2 bit counts + // 2 bit integer, 3 bit count requires only a 2 bit integer. // So we add all bit pairs, then each nible, then each byte etc... n = (n & 0x55555555) + ((n & 0xaaaaaaaa) >> 1); n = (n & 0x33333333) + ((n & 0xcccccccc) >> 2); n = (n & 0x0f0f0f0f) + ((n & 0xf0f0f0f0) >> 4); n = (n & 0x00ff00ff) + ((n & 0xff00ff00) >> 8); n = (n & 0x0000ffff) + ((n & 0xffff0000) >> 16); - // Etc for larger intergers (64 bits in Java) + // Etc for larger integers (64 bits in Java) // NOTE: the >> operation must be unsigned! (>>> in java) return n; } @@ -70,9 +54,44 @@ subject to the following restrictions: return (bits * 0x01010101) >> 24; } + // "Population Count (Ones Count) + // The population count of a binary integer value x is the number of one bits in the value. Although many machines have + // single instructions for this, the single instructions are usually microcoded loops that test a bit per cycle; a log-time + // algorithm coded in C is often faster. The following code uses a variable-precision SWAR algorithm to perform a tree + // reduction adding the bits in a 32-bit value:" + inline_ udword ones32(udword x) + { + /* 32-bit recursive reduction using SWAR... + but first step is mapping 2-bit values + into sum of 2 1-bit values in sneaky way + */ + x -= ((x >> 1) & 0x55555555); + x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); + x = (((x >> 4) + x) & 0x0f0f0f0f); + x += (x >> 8); + x += (x >> 16); + return (x & 0x0000003f); + // "It is worthwhile noting that the SWAR population count algorithm given above can be improved upon for the case of + // counting the population of multi-word bit sets. How? The last few steps in the reduction are using only a portion + // of the SWAR width to produce their results; thus, it would be possible to combine these steps across multiple words + // being reduced. One additional note: the AMD Athlon optimization guidelines suggest a very similar algorithm that + // replaces the last three lines with return((x * 0x01010101) >> 24);. For the Athlon (which has a very fast integer + // multiply), I would have expected AMD's code to be faster... but it is actually 6% slower according to my benchmarks + // using a 1.2GHz Athlon (a Thunderbird). Why? Well, it so happens that GCC doesn't use a multiply instruction - it + // writes out the equivalent shift and add sequence!" + } + + // "Trailing Zero Count + // Given the Least Significant 1 Bit and Population Count (Ones Count) algorithms, it is trivial to combine them to + // construct a trailing zero count (as pointed-out by Joe Bowbeer):" + inline_ udword tzc(sdword x) + { + return(ones32((x & -x) - 1)); + } + //! Spread out bits. EG 00001111 -> 0101010101 //! 00001010 -> 0100010000 - //! This is used to interleve to intergers to produce a `Morten Key' + //! This is used to interleave two integers to produce a `Morton Key' //! used in Space Filling Curves (See DrDobbs Journal, July 1999) //! Order is important. inline_ void SpreadBits(udword& n) @@ -84,12 +103,12 @@ subject to the following restrictions: n = ( n & 0x11111111) | (( n & 0x22222222) << 1); } - // Next Largest Power of 2 + // "Next Largest Power of 2 // Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm // that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with // the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next - // largest power of 2. For a 32-bit value: - inline_ udword nlpo2(udword x) + // largest power of 2. For a 32-bit value:" + inline_ udword NextPowerOfTwo(udword x) { x |= (x >> 1); x |= (x >> 2); @@ -131,24 +150,45 @@ subject to the following restrictions: inline_ char LittleEndian() { int i = 1; return *((char*)&i); } //!< Alternative abs function - inline_ udword abs_(sdword x) { sdword y= x >> 31; return (x^y)-y; } + inline_ udword abs_(sdword x) { sdword y= x >> 31; return (x^y)-y; } + + // "Integer Minimum or Maximum + // Given 2's complement integer values x and y, the minimum can be computed without any branches as + // x+(((y-x)>>(WORDBITS-1))&(y-x)). + // Logically, this works because the shift by (WORDBITS-1) replicates the sign bit to create a mask + // -- be aware, however, that the C language does not require that shifts are signed even if their + // operands are signed, so there is a potential portability problem. Additionally, one might think + // that a shift by any number greater than or equal to WORDBITS would have the same effect, but many + // instruction sets have shifts that behave strangely when such shift distances are specified. + // Of course, maximum can be computed using the same trick: + // x-(((x-y)>>(WORDBITS-1))&(x-y))." //!< Alternative min function inline_ sdword min_(sdword a, sdword b) { sdword delta = b-a; return a + (delta&(delta>>31)); } + //!< Alternative max function + inline_ sdword max_(sdword a, sdword b) { sdword delta = a-b; return a - (delta&(delta>>31)); } + + // "Integer Selection + // A branchless, lookup-free, alternative to code like if (a> (WORDBITS-1)) & (c^d)) ^ d). + // This code assumes that the shift is signed, which, of course, C does not promise." + inline_ sdword IntegerSelection(sdword a, sdword b, sdword c, sdword d) + { + return ((((a-b)>>31) & (c^d)) ^ d); + } // Determine if one of the bytes in a 4 byte word is zero - inline_ BOOL HasNullByte(udword x) { return ((x + 0xfefefeff) & (~x) & 0x80808080); } + inline_ BOOL HasNullByte(udword x) { return ((x + 0xfefefeff) & (~x) & 0x80808080); } // To find the smallest 1 bit in a word EG: ~~~~~~10---0 => 0----010---0 - inline_ udword LowestOneBit(udword w) { return ((w) & (~(w)+1)); } -// inline_ udword LowestOneBit_(udword w) { return ((w) & (-(w))); } + inline_ udword LowestOneBit(udword w) { return ((w) & (~(w)+1)); } +// inline_ udword LowestOneBit_(udword w) { return ((w) & (-(w))); } - // Most Significant 1 Bit + // "Most Significant 1 Bit // Given a binary integer value x, the most significant 1 bit (highest numbered element of a bit set) // can be computed using a SWAR algorithm that recursively "folds" the upper bits into the lower bits. // This process yields a bit vector with the same most significant 1 as x, but all 1's below it. // Bitwise AND of the original value with the complement of the "folded" value shifted down by one - // yields the most significant bit. For a 32-bit value: + // yields the most significant bit. For a 32-bit value:" inline_ udword msb32(udword x) { x |= (x >> 1); @@ -159,6 +199,23 @@ subject to the following restrictions: return (x & ~(x >> 1)); } + // "Gray Code Conversion + // A Gray code is any binary coding sequence in which only a single bit position changes as we move from one value to the next. + // There are many such codes, but the traditional one is computed such that the Kth Gray code is K^(K>>1). + // + // The well-known algorithm for conversion from Gray to binary is a linear sequence of XORs that makes it seem each bit must be + // dealt with separately. Fortunately, that is equivalent to a parallel prefix XOR that can be computed using SWAR techniques + // in log time. For 32-bit Gray code values produced as described above, the conversion from Gray code back to unsigned binary is:" + inline_ udword g2b(udword gray) + { + gray ^= (gray >> 16); + gray ^= (gray >> 8); + gray ^= (gray >> 4); + gray ^= (gray >> 2); + gray ^= (gray >> 1); + return gray; + } + /* "Just call it repeatedly with various input values and always with the same variable as "memory". The sharpness determines the degree of filtering, where 0 completely filters out the input, and 1 @@ -181,9 +238,9 @@ subject to the following restrictions: return memory = val * sharpness + memory * (1.0f - sharpness); } - //! If you can guarantee that your input domain (i.e. value of x) is slightly + //! "If you can guarantee that your input domain (i.e. value of x) is slightly //! limited (abs(x) must be < ((1<<31u)-32767)), then you can use the - //! following code to clamp the resulting value into [-32768,+32767] range: + //! following code to clamp the resulting value into [-32768,+32767] range:" inline_ int ClampToInt16(int x) { // ASSERT(abs(x) < (int)((1<<31u)-32767)); @@ -219,8 +276,12 @@ subject to the following restrictions: //! TO BE DOCUMENTED #define OFFSET_OF(Class, Member) (size_t)&(((Class*)0)->Member) + //! TO BE DOCUMENTED - #define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) + #if !defined(_XBOX) + // Already defined on Xbox. + #define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) + #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -236,7 +297,11 @@ subject to the following restrictions: #define IS_ALIGNED_4(x) ((x&3)==0) #define IS_ALIGNED_8(x) ((x&7)==0) - inline_ void _prefetch(void const* ptr) { (void)*(char const volatile *)ptr; } + // Updates a pointer with "stride" bytes + inline_ void UpdatePtr(void*& ptr, udword stride) { ptr = ((ubyte*)ptr) + stride; } + + // From Jon Watte IIRC + inline_ void _prefetch(void const* ptr) { (void)*(char const volatile *)ptr; } // Compute implicit coords from an index: // The idea is to get back 2D coords from a 1D index. @@ -269,4 +334,44 @@ subject to the following restrictions: Compute2DCoords(u, v, i - (w * nbu_nbv), nbu); } -#endif // __ICEUTILS_H__ + // Calling fsincos instead of fsin+fcos. Twice faster. + inline_ void FSinCos(float& c, float& s, float f) + { + float LocalCos, LocalSin; + float Local = f; +#ifdef WIN32 + _asm fld Local + _asm fsincos + _asm fstp LocalCos + _asm fstp LocalSin +#elif LINUX + asm("fld Local\n\t" + "fsincos\n\t" + "fstp LocalCos\n\t" + "fstp LocalSin\n\t" + ); +#endif + c = LocalCos; + s = LocalSin; + } + + // Modulo3 macros. See http://www.codercorner.com/Modulo3.htm + #define GET_NEXT_INDICES(i, j, k) \ + k = 0x01000201; \ + k>>=(i<<3); \ + j = k & 0xff; \ + k>>=8; \ + k&=0xff; + + #define GET_NEXT_INDICES2(i, j, k) \ + j = ( 9 >> (i<<1)) & 3; \ + k = (18 >> (i<<1)) & 3; + + // 0=>1, 1=>2, 2=>0 + inline_ udword Modulo3(udword i) + { + ASSERT(i==0 || i==1 || i==2); + return (9 >> (i << 1)) & 3; + } + +#endif // ICEUTILS_H diff --git a/Extras/CDTestFramework/Opcode/Ice/_IceAABB.h b/Extras/CDTestFramework/Opcode/Ice/_IceAABB.h new file mode 100644 index 000000000..10ff34b67 --- /dev/null +++ b/Extras/CDTestFramework/Opcode/Ice/_IceAABB.h @@ -0,0 +1,522 @@ +/* + * ICE / OPCODE - Optimized Collision Detection + * http://www.codercorner.com/Opcode.htm + * + * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains AABB-related code. (axis-aligned bounding box) + * \file IceAABB.h + * \author Pierre Terdiman + * \date January, 13, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEAABB_H__ +#define __ICEAABB_H__ + + // Forward declarations + class Sphere; + +//! Declarations of type-independent methods (most of them implemented in the .cpp) +#define AABB_COMMON_METHODS \ + AABB& Add(const AABB& aabb); \ + float MakeCube(AABB& cube) const; \ + void MakeSphere(Sphere& sphere) const; \ + const sbyte* ComputeOutline(const Point& local_eye, sdword& num) const; \ + float ComputeBoxArea(const Point& eye, const Matrix4x4& mat, float width, float height, sdword& num) const; \ + bool IsInside(const AABB& box) const; \ + bool ComputePlanes(Plane* planes) const; \ + bool ComputePoints(Point* pts) const; \ + const Point* GetVertexNormals() const; \ + const udword* GetEdges() const; \ + const Point* GetEdgeNormals() const; \ + inline_ BOOL ContainsPoint(const Point& p) const \ + { \ + if(p.x > GetMax(0) || p.x < GetMin(0)) return FALSE; \ + if(p.y > GetMax(1) || p.y < GetMin(1)) return FALSE; \ + if(p.z > GetMax(2) || p.z < GetMin(2)) return FALSE; \ + return TRUE; \ + } + + enum AABBType + { + AABB_RENDER = 0, //!< AABB used for rendering. Not visible == not rendered. + AABB_UPDATE = 1, //!< AABB used for dynamic updates. Not visible == not updated. + + AABB_FORCE_DWORD = 0x7fffffff, + }; + +#ifdef USE_MINMAX + + struct ICEMATHS_API ShadowAABB + { + Point mMin; + Point mMax; + }; + + class ICEMATHS_API AABB + { + public: + //! Constructor + inline_ AABB() {} + //! Destructor + inline_ ~AABB() {} + + //! Type-independent methods + AABB_COMMON_METHODS; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an AABB from min & max vectors. + * \param min [in] the min point + * \param max [in] the max point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetMinMax(const Point& min, const Point& max) { mMin = min; mMax = max; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an AABB from center & extents vectors. + * \param c [in] the center point + * \param e [in] the extents vector + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetCenterExtents(const Point& c, const Point& e) { mMin = c - e; mMax = c + e; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an empty AABB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetEmpty() { Point p(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); mMin = -p; mMax = p;} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups a point AABB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetPoint(const Point& pt) { mMin = mMax = pt; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the size of the AABB. The size is defined as the longest extent. + * \return the size of the AABB + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + float GetSize() const { Point e; GetExtents(e); return e.Max(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Extends the AABB. + * \param p [in] the next point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Extend(const Point& p) + { + if(p.x > mMax.x) mMax.x = p.x; + if(p.x < mMin.x) mMin.x = p.x; + + if(p.y > mMax.y) mMax.y = p.y; + if(p.y < mMin.y) mMin.y = p.y; + + if(p.z > mMax.z) mMax.z = p.z; + if(p.z < mMin.z) mMin.z = p.z; + } + // Data access + + //! Get min point of the box + inline_ void GetMin(Point& min) const { min = mMin; } + //! Get max point of the box + inline_ void GetMax(Point& max) const { max = mMax; } + + //! Get component of the box's min point along a given axis + inline_ float GetMin(udword axis) const { return mMin[axis]; } + //! Get component of the box's max point along a given axis + inline_ float GetMax(udword axis) const { return mMax[axis]; } + + //! Get box center + inline_ void GetCenter(Point& center) const { center = (mMax + mMin)*0.5f; } + //! Get box extents + inline_ void GetExtents(Point& extents) const { extents = (mMax - mMin)*0.5f; } + + //! Get component of the box's center along a given axis + inline_ float GetCenter(udword axis) const { return (mMax[axis] + mMin[axis])*0.5f; } + //! Get component of the box's extents along a given axis + inline_ float GetExtents(udword axis) const { return (mMax[axis] - mMin[axis])*0.5f; } + + //! Get box diagonal + inline_ void GetDiagonal(Point& diagonal) const { diagonal = mMax - mMin; } + inline_ float GetWidth() const { return mMax.x - mMin.x; } + inline_ float GetHeight() const { return mMax.y - mMin.y; } + inline_ float GetDepth() const { return mMax.z - mMin.z; } + + //! Volume + inline_ float GetVolume() const { return GetWidth() * GetHeight() * GetDepth(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the intersection between two AABBs. + * \param a [in] the other AABB + * \return true on intersection + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Intersect(const AABB& a) const + { + if(mMax.x < a.mMin.x + || a.mMax.x < mMin.x + || mMax.y < a.mMin.y + || a.mMax.y < mMin.y + || mMax.z < a.mMin.z + || a.mMax.z < mMin.z) return FALSE; + + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the 1D-intersection between two AABBs, on a given axis. + * \param a [in] the other AABB + * \param axis [in] the axis (0, 1, 2) + * \return true on intersection + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Intersect(const AABB& a, udword axis) const + { + if(mMax[axis] < a.mMin[axis] || a.mMax[axis] < mMin[axis]) return FALSE; + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Recomputes the AABB after an arbitrary transform by a 4x4 matrix. + * Original code by Charles Bloom on the GD-Algorithm list. (I slightly modified it) + * \param mtx [in] the transform matrix + * \param aabb [out] the transformed AABB [can be *this] + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void Rotate(const Matrix4x4& mtx, AABB& aabb) const + { + // The three edges transformed: you can efficiently transform an X-only vector + // by just getting the "X" column of the matrix + Point vx,vy,vz; + mtx.GetRow(0, vx); vx *= (mMax.x - mMin.x); + mtx.GetRow(1, vy); vy *= (mMax.y - mMin.y); + mtx.GetRow(2, vz); vz *= (mMax.z - mMin.z); + + // Transform the min point + aabb.mMin = aabb.mMax = mMin * mtx; + + // Take the transformed min & axes and find new extents + // Using CPU code in the right place is faster... + if(IS_NEGATIVE_FLOAT(vx.x)) aabb.mMin.x += vx.x; else aabb.mMax.x += vx.x; + if(IS_NEGATIVE_FLOAT(vx.y)) aabb.mMin.y += vx.y; else aabb.mMax.y += vx.y; + if(IS_NEGATIVE_FLOAT(vx.z)) aabb.mMin.z += vx.z; else aabb.mMax.z += vx.z; + if(IS_NEGATIVE_FLOAT(vy.x)) aabb.mMin.x += vy.x; else aabb.mMax.x += vy.x; + if(IS_NEGATIVE_FLOAT(vy.y)) aabb.mMin.y += vy.y; else aabb.mMax.y += vy.y; + if(IS_NEGATIVE_FLOAT(vy.z)) aabb.mMin.z += vy.z; else aabb.mMax.z += vy.z; + if(IS_NEGATIVE_FLOAT(vz.x)) aabb.mMin.x += vz.x; else aabb.mMax.x += vz.x; + if(IS_NEGATIVE_FLOAT(vz.y)) aabb.mMin.y += vz.y; else aabb.mMax.y += vz.y; + if(IS_NEGATIVE_FLOAT(vz.z)) aabb.mMin.z += vz.z; else aabb.mMax.z += vz.z; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the AABB is valid. + * \return true if the box is valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsValid() const + { + // Consistency condition for (Min, Max) boxes: min < max + if(mMin.x > mMax.x) return FALSE; + if(mMin.y > mMax.y) return FALSE; + if(mMin.z > mMax.z) return FALSE; + return TRUE; + } + + //! Operator for AABB *= float. Scales the extents, keeps same center. + inline_ AABB& operator*=(float s) + { + Point Center; GetCenter(Center); + Point Extents; GetExtents(Extents); + SetCenterExtents(Center, Extents * s); + return *this; + } + + //! Operator for AABB /= float. Scales the extents, keeps same center. + inline_ AABB& operator/=(float s) + { + Point Center; GetCenter(Center); + Point Extents; GetExtents(Extents); + SetCenterExtents(Center, Extents / s); + return *this; + } + + //! Operator for AABB += Point. Translates the box. + inline_ AABB& operator+=(const Point& trans) + { + mMin+=trans; + mMax+=trans; + return *this; + } + private: + Point mMin; //!< Min point + Point mMax; //!< Max point + }; + +#else + + class ICEMATHS_API AABB + { + public: + //! Constructor + inline_ AABB() {} + //! Destructor + inline_ ~AABB() {} + + //! Type-independent methods + AABB_COMMON_METHODS; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an AABB from min & max vectors. + * \param min [in] the min point + * \param max [in] the max point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetMinMax(const Point& min, const Point& max) { mCenter = (max + min)*0.5f; mExtents = (max - min)*0.5f; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an AABB from center & extents vectors. + * \param c [in] the center point + * \param e [in] the extents vector + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetCenterExtents(const Point& c, const Point& e) { mCenter = c; mExtents = e; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an empty AABB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetEmpty() { mCenter.Zero(); mExtents.Set(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups a point AABB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetPoint(const Point& pt) { mCenter = pt; mExtents.Zero(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the size of the AABB. The size is defined as the longest extent. + * \return the size of the AABB + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + float GetSize() const { return mExtents.Max(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Extends the AABB. + * \param p [in] the next point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Extend(const Point& p) + { + Point Max = mCenter + mExtents; + Point Min = mCenter - mExtents; + + if(p.x > Max.x) Max.x = p.x; + if(p.x < Min.x) Min.x = p.x; + + if(p.y > Max.y) Max.y = p.y; + if(p.y < Min.y) Min.y = p.y; + + if(p.z > Max.z) Max.z = p.z; + if(p.z < Min.z) Min.z = p.z; + + SetMinMax(Min, Max); + } + // Data access + + //! Get min point of the box + inline_ void GetMin(Point& min) const { min = mCenter - mExtents; } + //! Get max point of the box + inline_ void GetMax(Point& max) const { max = mCenter + mExtents; } + + //! Get component of the box's min point along a given axis + inline_ float GetMin(udword axis) const { return mCenter[axis] - mExtents[axis]; } + //! Get component of the box's max point along a given axis + inline_ float GetMax(udword axis) const { return mCenter[axis] + mExtents[axis]; } + + //! Get box center + inline_ void GetCenter(Point& center) const { center = mCenter; } + //! Get box extents + inline_ void GetExtents(Point& extents) const { extents = mExtents; } + + //! Get component of the box's center along a given axis + inline_ float GetCenter(udword axis) const { return mCenter[axis]; } + //! Get component of the box's extents along a given axis + inline_ float GetExtents(udword axis) const { return mExtents[axis]; } + + //! Get box diagonal + inline_ void GetDiagonal(Point& diagonal) const { diagonal = mExtents * 2.0f; } + inline_ float GetWidth() const { return mExtents.x * 2.0f; } + inline_ float GetHeight() const { return mExtents.y * 2.0f; } + inline_ float GetDepth() const { return mExtents.z * 2.0f; } + + //! Volume + inline_ float GetVolume() const { return mExtents.x * mExtents.y * mExtents.z * 8.0f; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the intersection between two AABBs. + * \param a [in] the other AABB + * \return true on intersection + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Intersect(const AABB& a) const + { + float tx = mCenter.x - a.mCenter.x; float ex = a.mExtents.x + mExtents.x; if(AIR(tx) > IR(ex)) return FALSE; + float ty = mCenter.y - a.mCenter.y; float ey = a.mExtents.y + mExtents.y; if(AIR(ty) > IR(ey)) return FALSE; + float tz = mCenter.z - a.mCenter.z; float ez = a.mExtents.z + mExtents.z; if(AIR(tz) > IR(ez)) return FALSE; + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * The standard intersection method from Gamasutra. Just here to check its speed against the one above. + * \param a [in] the other AABB + * \return true on intersection + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool GomezIntersect(const AABB& a) + { + Point T = mCenter - a.mCenter; // Vector from A to B + return ((fabsf(T.x) <= (a.mExtents.x + mExtents.x)) + && (fabsf(T.y) <= (a.mExtents.y + mExtents.y)) + && (fabsf(T.z) <= (a.mExtents.z + mExtents.z))); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the 1D-intersection between two AABBs, on a given axis. + * \param a [in] the other AABB + * \param axis [in] the axis (0, 1, 2) + * \return true on intersection + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Intersect(const AABB& a, udword axis) const + { + float t = mCenter[axis] - a.mCenter[axis]; + float e = a.mExtents[axis] + mExtents[axis]; + if(AIR(t) > IR(e)) return FALSE; + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Recomputes the AABB after an arbitrary transform by a 4x4 matrix. + * \param mtx [in] the transform matrix + * \param aabb [out] the transformed AABB [can be *this] + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void Rotate(const Matrix4x4& mtx, AABB& aabb) const + { + // Compute new center + aabb.mCenter = mCenter * mtx; + + // Compute new extents. FPU code & CPU code have been interleaved for improved performance. + Point Ex(mtx.m[0][0] * mExtents.x, mtx.m[0][1] * mExtents.x, mtx.m[0][2] * mExtents.x); + IR(Ex.x)&=0x7fffffff; IR(Ex.y)&=0x7fffffff; IR(Ex.z)&=0x7fffffff; + + Point Ey(mtx.m[1][0] * mExtents.y, mtx.m[1][1] * mExtents.y, mtx.m[1][2] * mExtents.y); + IR(Ey.x)&=0x7fffffff; IR(Ey.y)&=0x7fffffff; IR(Ey.z)&=0x7fffffff; + + Point Ez(mtx.m[2][0] * mExtents.z, mtx.m[2][1] * mExtents.z, mtx.m[2][2] * mExtents.z); + IR(Ez.x)&=0x7fffffff; IR(Ez.y)&=0x7fffffff; IR(Ez.z)&=0x7fffffff; + + aabb.mExtents.x = Ex.x + Ey.x + Ez.x; + aabb.mExtents.y = Ex.y + Ey.y + Ez.y; + aabb.mExtents.z = Ex.z + Ey.z + Ez.z; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the AABB is valid. + * \return true if the box is valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsValid() const + { + // Consistency condition for (Center, Extents) boxes: Extents >= 0 + if(IS_NEGATIVE_FLOAT(mExtents.x)) return FALSE; + if(IS_NEGATIVE_FLOAT(mExtents.y)) return FALSE; + if(IS_NEGATIVE_FLOAT(mExtents.z)) return FALSE; + return TRUE; + } + + //! Operator for AABB *= float. Scales the extents, keeps same center. + inline_ AABB& operator*=(float s) { mExtents*=s; return *this; } + + //! Operator for AABB /= float. Scales the extents, keeps same center. + inline_ AABB& operator/=(float s) { mExtents/=s; return *this; } + + //! Operator for AABB += Point. Translates the box. + inline_ AABB& operator+=(const Point& trans) + { + mCenter+=trans; + return *this; + } + private: + Point mCenter; //!< AABB Center + Point mExtents; //!< x, y and z extents + }; + +#endif + + inline_ void ComputeMinMax(const Point& p, Point& min, Point& max) + { + if(p.x > max.x) max.x = p.x; + if(p.x < min.x) min.x = p.x; + + if(p.y > max.y) max.y = p.y; + if(p.y < min.y) min.y = p.y; + + if(p.z > max.z) max.z = p.z; + if(p.z < min.z) min.z = p.z; + } + + inline_ void ComputeAABB(AABB& aabb, const Point* list, udword nb_pts) + { + if(list) + { + Point Maxi(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); + Point Mini(MAX_FLOAT, MAX_FLOAT, MAX_FLOAT); + while(nb_pts--) + { +// _prefetch(list+1); // off by one ? + ComputeMinMax(*list++, Mini, Maxi); + } + aabb.SetMinMax(Mini, Maxi); + } + } + +#endif // __ICEAABB_H__ diff --git a/Extras/CDTestFramework/Opcode/Ice/_IceContainer.cpp b/Extras/CDTestFramework/Opcode/Ice/_IceContainer.cpp new file mode 100644 index 000000000..53c2b4465 --- /dev/null +++ b/Extras/CDTestFramework/Opcode/Ice/_IceContainer.cpp @@ -0,0 +1,361 @@ +/* + * ICE / OPCODE - Optimized Collision Detection + * http://www.codercorner.com/Opcode.htm + * + * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a simple container class. + * \file IceContainer.cpp + * \author Pierre Terdiman + * \date February, 5, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a list of 32-bits values. + * Use this class when you need to store an unknown number of values. The list is automatically + * resized and can contains 32-bits entities (dwords or floats) + * + * \class Container + * \author Pierre Terdiman + * \version 1.0 + * \date 08.15.98 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceCore; + +// Static members +#ifdef CONTAINER_STATS +udword Container::mNbContainers = 0; +udword Container::mUsedRam = 0; +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. No entries allocated there. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Container::Container() : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2.0f) +{ +#ifdef CONTAINER_STATS + mNbContainers++; + mUsedRam+=sizeof(Container); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. Also allocates a given number of entries. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Container::Container(udword size, float growth_factor) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(growth_factor) +{ +#ifdef CONTAINER_STATS + mNbContainers++; + mUsedRam+=sizeof(Container); +#endif + SetSize(size); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Copy constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Container::Container(const Container& object) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2.0f) +{ +#ifdef CONTAINER_STATS + mNbContainers++; + mUsedRam+=sizeof(Container); +#endif + *this = object; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. Frees everything and leaves. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Container::~Container() +{ + Empty(); +#ifdef CONTAINER_STATS + mNbContainers--; + mUsedRam-=GetUsedRam(); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Clears the container. All stored values are deleted, and it frees used ram. + * \see Reset() + * \return Self-Reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Container& Container::Empty() +{ +#ifdef CONTAINER_STATS + mUsedRam-=mMaxNbEntries*sizeof(udword); +#endif + DELETEARRAY(mEntries); + mCurNbEntries = mMaxNbEntries = 0; + return *this; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Resizes the container. + * \param needed [in] assume the container can be added at least "needed" values + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Container::Resize(udword needed) +{ +#ifdef CONTAINER_STATS + // Subtract previous amount of bytes + mUsedRam-=mMaxNbEntries*sizeof(udword); +#endif + + // Get more entries + mMaxNbEntries = mMaxNbEntries ? udword(float(mMaxNbEntries)*mGrowthFactor) : 2; // Default nb Entries = 2 + if(mMaxNbEntriesmMaxNbEntries) Resize(nb); + + // Add new entry + CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(udword)); + mCurNbEntries+=nb; + return *this; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * A O(1) method to add a value in the container. The container is automatically resized if needed. + * The method is inline, not the resize. The call overhead happens on resizes only, which is not a problem since the resizing operation + * costs a lot more than the call overhead... + * + * \param entry [in] a float to store in the container + * \see Add(udword entry) + * \see Empty() + * \see Contains(udword entry) + * \return Self-Reference + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ Container& Add(float entry) + { + // Resize if needed + if(mCurNbEntries==mMaxNbEntries) Resize(); + + // Add new entry + mEntries[mCurNbEntries++] = IR(entry); + return *this; + } + + inline_ Container& Add(const float* entries, udword nb) + { + // Resize if needed + if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb); + + // Add new entry + CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(float)); + mCurNbEntries+=nb; + return *this; + } + + //! Add unique [slow] + inline_ Container& AddUnique(udword entry) + { + if(!Contains(entry)) Add(entry); + return *this; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Clears the container. All stored values are deleted, and it frees used ram. + * \see Reset() + * \return Self-Reference + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + Container& Empty(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Resets the container. Stored values are discarded but the buffer is kept so that further calls don't need resizing again. + * That's a kind of temporal coherence. + * \see Empty() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void Reset() + { + // Avoid the write if possible + // ### CMOV + if(mCurNbEntries) mCurNbEntries = 0; + } + + // HANDLE WITH CARE + inline_ void ForceSize(udword size) + { + mCurNbEntries = size; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Sets the initial size of the container. If it already contains something, it's discarded. + * \param nb [in] Number of entries + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool SetSize(udword nb); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Refits the container and get rid of unused bytes. + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Refit(); + + // Checks whether the container already contains a given value. + bool Contains(udword entry, udword* location=null) const; + // Deletes an entry - doesn't preserve insertion order. + bool Delete(udword entry); + // Deletes an entry - does preserve insertion order. + bool DeleteKeepingOrder(udword entry); + //! Deletes the very last entry. + inline_ void DeleteLastEntry() { if(mCurNbEntries) mCurNbEntries--; } + //! Deletes the entry whose index is given + inline_ void DeleteIndex(udword index) { mEntries[index] = mEntries[--mCurNbEntries]; } + + // Helpers + Container& FindNext(udword& entry, FindMode find_mode=FIND_CLAMP); + Container& FindPrev(udword& entry, FindMode find_mode=FIND_CLAMP); + // Data access. + inline_ udword GetNbEntries() const { return mCurNbEntries; } //!< Returns the current number of entries. + inline_ udword GetEntry(udword i) const { return mEntries[i]; } //!< Returns ith entry + inline_ udword* GetEntries() const { return mEntries; } //!< Returns the list of entries. + + inline_ udword GetFirst() const { return mEntries[0]; } + inline_ udword GetLast() const { return mEntries[mCurNbEntries-1]; } + + // Growth control + inline_ float GetGrowthFactor() const { return mGrowthFactor; } //!< Returns the growth factor + inline_ void SetGrowthFactor(float growth) { mGrowthFactor = growth; } //!< Sets the growth factor + inline_ bool IsFull() const { return mCurNbEntries==mMaxNbEntries; } //!< Checks the container is full + inline_ BOOL IsNotEmpty() const { return mCurNbEntries; } //!< Checks the container is empty + + //! Read-access as an array + inline_ udword operator[](udword i) const { ASSERT(i>=0 && i=0 && i>31); + return (float&)y; + } + + //! Computes 1.0f / sqrtf(x). + inline_ float frsqrt(float f) + { + float x = f * 0.5f; + udword y = 0x5f3759df - ((udword&)f >> 1); + // Iteration... + (float&)y = (float&)y * ( 1.5f - ( x * (float&)y * (float&)y ) ); + // Result + return (float&)y; + } + + //! Computes 1.0f / sqrtf(x). Comes from NVIDIA. + inline_ float InvSqrt(const float& x) + { + udword tmp = (udword(IEEE_1_0 << 1) + IEEE_1_0 - *(udword*)&x) >> 1; + float y = *(float*)&tmp; + return y * (1.47f - 0.47f * x * y * y); + } + + //! Computes 1.0f / sqrtf(x). Comes from Quake3. Looks like the first one I had above. + //! See http://www.magic-software.com/3DGEDInvSqrt.html + inline_ float RSqrt(float number) + { + long i; + float x2, y; + const float threehalfs = 1.5f; + + x2 = number * 0.5f; + y = number; + i = * (long *) &y; + i = 0x5f3759df - (i >> 1); + y = * (float *) &i; + y = y * (threehalfs - (x2 * y * y)); + + return y; + } + + //! TO BE DOCUMENTED + inline_ float fsqrt(float f) + { + udword y = ( ( (sdword&)f - 0x3f800000 ) >> 1 ) + 0x3f800000; + // Iteration...? + // (float&)y = (3.0f - ((float&)y * (float&)y) / f) * (float&)y * 0.5f; + // Result + return (float&)y; + } + + //! Returns the float ranged espilon value. + inline_ float fepsilon(float f) + { + udword b = (udword&)f & 0xff800000; + udword a = b | 0x00000001; + (float&)a -= (float&)b; + // Result + return (float&)a; + } + + //! Is the float valid ? + inline_ bool IsNAN(float value) { return (IR(value)&0x7f800000) == 0x7f800000; } + inline_ bool IsIndeterminate(float value) { return IR(value) == 0xffc00000; } + inline_ bool IsPlusInf(float value) { return IR(value) == 0x7f800000; } + inline_ bool IsMinusInf(float value) { return IR(value) == 0xff800000; } + + inline_ bool IsValidFloat(float value) + { + if(IsNAN(value)) return false; + if(IsIndeterminate(value)) return false; + if(IsPlusInf(value)) return false; + if(IsMinusInf(value)) return false; + return true; + } + + #define CHECK_VALID_FLOAT(x) ASSERT(IsValidFloat(x)); + +/* + //! FPU precision setting function. + inline_ void SetFPU() + { + // This function evaluates whether the floating-point + // control word is set to single precision/round to nearest/ + // exceptions disabled. If these conditions don't hold, the + // function changes the control word to set them and returns + // TRUE, putting the old control word value in the passback + // location pointed to by pwOldCW. + { + uword wTemp, wSave; + + __asm fstcw wSave + if (wSave & 0x300 || // Not single mode + 0x3f != (wSave & 0x3f) || // Exceptions enabled + wSave & 0xC00) // Not round to nearest mode + { + __asm + { + mov ax, wSave + and ax, not 300h ;; single mode + or ax, 3fh ;; disable all exceptions + and ax, not 0xC00 ;; round to nearest mode + mov wTemp, ax + fldcw wTemp + } + } + } + } +*/ + //! This function computes the slowest possible floating-point value (you can also directly use FLT_EPSILON) + inline_ float ComputeFloatEpsilon() + { + float f = 1.0f; + ((udword&)f)^=1; + return f - 1.0f; // You can check it's the same as FLT_EPSILON + } + + inline_ bool IsFloatZero(float x, float epsilon=1e-6f) + { + return x*x < epsilon; + } + + #define FCOMI_ST0 _asm _emit 0xdb _asm _emit 0xf0 + #define FCOMIP_ST0 _asm _emit 0xdf _asm _emit 0xf0 + #define FCMOVB_ST0 _asm _emit 0xda _asm _emit 0xc0 + #define FCMOVNB_ST0 _asm _emit 0xdb _asm _emit 0xc0 + + #define FCOMI_ST1 _asm _emit 0xdb _asm _emit 0xf1 + #define FCOMIP_ST1 _asm _emit 0xdf _asm _emit 0xf1 + #define FCMOVB_ST1 _asm _emit 0xda _asm _emit 0xc1 + #define FCMOVNB_ST1 _asm _emit 0xdb _asm _emit 0xc1 + + #define FCOMI_ST2 _asm _emit 0xdb _asm _emit 0xf2 + #define FCOMIP_ST2 _asm _emit 0xdf _asm _emit 0xf2 + #define FCMOVB_ST2 _asm _emit 0xda _asm _emit 0xc2 + #define FCMOVNB_ST2 _asm _emit 0xdb _asm _emit 0xc2 + + #define FCOMI_ST3 _asm _emit 0xdb _asm _emit 0xf3 + #define FCOMIP_ST3 _asm _emit 0xdf _asm _emit 0xf3 + #define FCMOVB_ST3 _asm _emit 0xda _asm _emit 0xc3 + #define FCMOVNB_ST3 _asm _emit 0xdb _asm _emit 0xc3 + + #define FCOMI_ST4 _asm _emit 0xdb _asm _emit 0xf4 + #define FCOMIP_ST4 _asm _emit 0xdf _asm _emit 0xf4 + #define FCMOVB_ST4 _asm _emit 0xda _asm _emit 0xc4 + #define FCMOVNB_ST4 _asm _emit 0xdb _asm _emit 0xc4 + + #define FCOMI_ST5 _asm _emit 0xdb _asm _emit 0xf5 + #define FCOMIP_ST5 _asm _emit 0xdf _asm _emit 0xf5 + #define FCMOVB_ST5 _asm _emit 0xda _asm _emit 0xc5 + #define FCMOVNB_ST5 _asm _emit 0xdb _asm _emit 0xc5 + + #define FCOMI_ST6 _asm _emit 0xdb _asm _emit 0xf6 + #define FCOMIP_ST6 _asm _emit 0xdf _asm _emit 0xf6 + #define FCMOVB_ST6 _asm _emit 0xda _asm _emit 0xc6 + #define FCMOVNB_ST6 _asm _emit 0xdb _asm _emit 0xc6 + + #define FCOMI_ST7 _asm _emit 0xdb _asm _emit 0xf7 + #define FCOMIP_ST7 _asm _emit 0xdf _asm _emit 0xf7 + #define FCMOVB_ST7 _asm _emit 0xda _asm _emit 0xc7 + #define FCMOVNB_ST7 _asm _emit 0xdb _asm _emit 0xc7 + + //! A global function to find MAX(a,b) using FCOMI/FCMOV + inline_ float FCMax2(float a, float b) + { + float Res; + _asm fld [a] + _asm fld [b] + FCOMI_ST1 + FCMOVB_ST1 + _asm fstp [Res] + _asm fcomp + return Res; + } + + //! A global function to find MIN(a,b) using FCOMI/FCMOV + inline_ float FCMin2(float a, float b) + { + float Res; + _asm fld [a] + _asm fld [b] + FCOMI_ST1 + FCMOVNB_ST1 + _asm fstp [Res] + _asm fcomp + return Res; + } + + //! A global function to find MAX(a,b,c) using FCOMI/FCMOV + inline_ float FCMax3(float a, float b, float c) + { + float Res; + _asm fld [a] + _asm fld [b] + _asm fld [c] + FCOMI_ST1 + FCMOVB_ST1 + FCOMI_ST2 + FCMOVB_ST2 + _asm fstp [Res] + _asm fcompp + return Res; + } + + //! A global function to find MIN(a,b,c) using FCOMI/FCMOV + inline_ float FCMin3(float a, float b, float c) + { + float Res; + _asm fld [a] + _asm fld [b] + _asm fld [c] + FCOMI_ST1 + FCMOVNB_ST1 + FCOMI_ST2 + FCMOVNB_ST2 + _asm fstp [Res] + _asm fcompp + return Res; + } + + inline_ int ConvertToSortable(float f) + { + int& Fi = (int&)f; + int Fmask = (Fi>>31); + Fi ^= Fmask; + Fmask &= ~(1<<31); + Fi -= Fmask; + return Fi; + } + + enum FPUMode + { + FPU_FLOOR = 0, + FPU_CEIL = 1, + FPU_BEST = 2, + + FPU_FORCE_DWORD = 0x7fffffff + }; + + FUNCTION ICECORE_API FPUMode GetFPUMode(); + FUNCTION ICECORE_API void SaveFPU(); + FUNCTION ICECORE_API void RestoreFPU(); + FUNCTION ICECORE_API void SetFPUFloorMode(); + FUNCTION ICECORE_API void SetFPUCeilMode(); + FUNCTION ICECORE_API void SetFPUBestMode(); + + FUNCTION ICECORE_API void SetFPUPrecision24(); + FUNCTION ICECORE_API void SetFPUPrecision53(); + FUNCTION ICECORE_API void SetFPUPrecision64(); + FUNCTION ICECORE_API void SetFPURoundingChop(); + FUNCTION ICECORE_API void SetFPURoundingUp(); + FUNCTION ICECORE_API void SetFPURoundingDown(); + FUNCTION ICECORE_API void SetFPURoundingNear(); + + FUNCTION ICECORE_API int intChop(const float& f); + FUNCTION ICECORE_API int intFloor(const float& f); + FUNCTION ICECORE_API int intCeil(const float& f); + +#endif // __ICEFPU_H__ diff --git a/Extras/CDTestFramework/Opcode/Ice/_IceMemoryMacros.h b/Extras/CDTestFramework/Opcode/Ice/_IceMemoryMacros.h new file mode 100644 index 000000000..d845e52fb --- /dev/null +++ b/Extras/CDTestFramework/Opcode/Ice/_IceMemoryMacros.h @@ -0,0 +1,121 @@ +/* + * ICE / OPCODE - Optimized Collision Detection + * http://www.codercorner.com/Opcode.htm + * + * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains all memory macros. + * \file IceMemoryMacros.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEMEMORYMACROS_H__ +#define __ICEMEMORYMACROS_H__ + +#undef ZeroMemory +#undef CopyMemory +#undef MoveMemory +#undef FillMemory + + //! Clears a buffer. + //! \param addr [in] buffer address + //! \param size [in] buffer length + //! \see FillMemory + //! \see StoreDwords + //! \see CopyMemory + //! \see MoveMemory + inline_ void ZeroMemory(void* addr, udword size) { memset(addr, 0, size); } + + //! Fills a buffer with a given byte. + //! \param addr [in] buffer address + //! \param size [in] buffer length + //! \param val [in] the byte value + //! \see StoreDwords + //! \see ZeroMemory + //! \see CopyMemory + //! \see MoveMemory + inline_ void FillMemory(void* dest, udword size, ubyte val) { memset(dest, val, size); } + + //! Fills a buffer with a given dword. + //! \param addr [in] buffer address + //! \param nb [in] number of dwords to write + //! \param value [in] the dword value + //! \see FillMemory + //! \see ZeroMemory + //! \see CopyMemory + //! \see MoveMemory + //! \warning writes nb*4 bytes ! + inline_ void StoreDwords(udword* dest, udword nb, udword value) + { + // The asm code below **SHOULD** be equivalent to one of those C versions + // or the other if your compiled is good: (checked on VC++ 6.0) + // + // 1) while(nb--) *dest++ = value; + // + // 2) for(udword i=0;iRelease(); (x) = null; } //!< Safe D3D-style release + #define SAFE_DESTRUCT(x) if (x) { (x)->SelfDestruct(); (x) = null; } //!< Safe ICE-style release + +#ifdef __ICEERROR_H__ + #define CHECKALLOC(x) if(!x) return SetIceError("Out of memory.", EC_OUT_OF_MEMORY); //!< Standard alloc checking. HANDLE WITH CARE. +#else + #define CHECKALLOC(x) if(!x) return false; +#endif + + //! Standard allocation cycle + #define SAFE_ALLOC(ptr, type, count) DELETEARRAY(ptr); ptr = new type[count]; CHECKALLOC(ptr); + +#endif // __ICEMEMORYMACROS_H__ diff --git a/Extras/CDTestFramework/Opcode/Ice/_IcePreprocessor.h b/Extras/CDTestFramework/Opcode/Ice/_IcePreprocessor.h new file mode 100644 index 000000000..e3a2d1173 --- /dev/null +++ b/Extras/CDTestFramework/Opcode/Ice/_IcePreprocessor.h @@ -0,0 +1,144 @@ +/* + * ICE / OPCODE - Optimized Collision Detection + * http://www.codercorner.com/Opcode.htm + * + * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains preprocessor stuff. This should be the first included header. + * \file IcePreprocessor.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEPREPROCESSOR_H__ +#define __ICEPREPROCESSOR_H__ + + // Check platform + #if defined( _WIN32 ) || defined( WIN32 ) + #pragma message("Compiling on Windows...") + #define PLATFORM_WINDOWS + #else + #pragma message("Compiling on unknown platform...") + #endif + + // Check compiler + #if defined(_MSC_VER) + #pragma message("Compiling with VC++...") + #define COMPILER_VISUAL_CPP + #else + #pragma message("Compiling with unknown compiler...") + #endif + + // Check compiler options. If this file is included in user-apps, this + // shouldn't be needed, so that they can use what they like best. + #ifndef ICE_DONT_CHECK_COMPILER_OPTIONS + #ifdef COMPILER_VISUAL_CPP + #if defined(_CHAR_UNSIGNED) + #endif + + #if defined(_CPPRTTI) + #error Please disable RTTI... + #endif + + #if defined(_CPPUNWIND) + #error Please disable exceptions... + #endif + + #if defined(_MT) + // Multithreading + #endif + #endif + #endif + + // Check debug mode + #ifdef DEBUG // May be defined instead of _DEBUG. Let's fix it. + #ifndef _DEBUG + #define _DEBUG + #endif + #endif + + #ifdef _DEBUG + // Here you may define items for debug builds + #endif + + #ifndef THIS_FILE + #define THIS_FILE __FILE__ + #endif + + #ifndef ICE_NO_DLL + #ifdef ICECORE_EXPORTS + #define ICECORE_API __declspec(dllexport) + #else + #define ICECORE_API __declspec(dllimport) + #endif + #else + #define ICECORE_API + #endif + + // Don't override new/delete +// #define DEFAULT_NEWDELETE + #define DONT_TRACK_MEMORY_LEAKS + + #define FUNCTION extern "C" + + // Cosmetic stuff [mainly useful with multiple inheritance] + #define override(base_class) virtual + + // Our own inline keyword, so that: + // - we can switch to __forceinline to check it's really better or not + // - we can remove __forceinline if the compiler doesn't support it +// #define inline_ __forceinline +// #define inline_ inline + + // Contributed by Bruce Mitchener + #if defined(COMPILER_VISUAL_CPP) + #define inline_ __forceinline +// #define inline_ inline + #elif defined(__GNUC__) && __GNUC__ < 3 + #define inline_ inline + #elif defined(__GNUC__) + #define inline_ inline __attribute__ ((always_inline)) + #else + #define inline_ inline + #endif + + // Down the hatch + #pragma inline_depth( 255 ) + + #ifdef COMPILER_VISUAL_CPP + #pragma intrinsic(memcmp) + #pragma intrinsic(memcpy) + #pragma intrinsic(memset) + #pragma intrinsic(strcat) + #pragma intrinsic(strcmp) + #pragma intrinsic(strcpy) + #pragma intrinsic(strlen) + #pragma intrinsic(abs) + #pragma intrinsic(labs) + #endif + + // ANSI compliance + #ifdef _DEBUG + // Remove painful warning in debug + inline_ bool __False__(){ return false; } + #define for if(__False__()){} else for + #else + #define for if(0){} else for + #endif + +#endif // __ICEPREPROCESSOR_H__ diff --git a/Extras/CDTestFramework/Opcode/Ice/_IceRevisitedRadix.cpp b/Extras/CDTestFramework/Opcode/Ice/_IceRevisitedRadix.cpp new file mode 100644 index 000000000..3a77b4a22 --- /dev/null +++ b/Extras/CDTestFramework/Opcode/Ice/_IceRevisitedRadix.cpp @@ -0,0 +1,536 @@ +/* + * ICE / OPCODE - Optimized Collision Detection + * http://www.codercorner.com/Opcode.htm + * + * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains source code from the article "Radix Sort Revisited". + * \file IceRevisitedRadix.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Revisited Radix Sort. + * This is my new radix routine: + * - it uses indices and doesn't recopy the values anymore, hence wasting less ram + * - it creates all the histograms in one run instead of four + * - it sorts words faster than dwords and bytes faster than words + * - it correctly sorts negative floating-point values by patching the offsets + * - it automatically takes advantage of temporal coherence + * - multiple keys support is a side effect of temporal coherence + * - it may be worth recoding in asm... (mainly to use FCOMI, FCMOV, etc) [it's probably memory-bound anyway] + * + * History: + * - 08.15.98: very first version + * - 04.04.00: recoded for the radix article + * - 12.xx.00: code lifting + * - 09.18.01: faster CHECK_PASS_VALIDITY thanks to Mark D. Shattuck (who provided other tips, not included here) + * - 10.11.01: added local ram support + * - 01.20.02: bugfix! In very particular cases the last pass was skipped in the float code-path, leading to incorrect sorting...... + * - 01.02.02: - "mIndices" renamed => "mRanks". That's a rank sorter after all. + * - ranks are not "reset" anymore, but implicit on first calls + * - 07.05.02: - offsets rewritten with one less indirection. + * - 11.03.02: - "bool" replaced with RadixHint enum + * + * \class RadixSort + * \author Pierre Terdiman + * \version 1.4 + * \date August, 15, 1998 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* +To do: + - add an offset parameter between two input values (avoid some data recopy sometimes) + - unroll ? asm ? + - 11 bits trick & 3 passes as Michael did + - prefetch stuff the day I have a P3 + - make a version with 16-bits indices ? +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceCore; + +#define INVALIDATE_RANKS mCurrentSize|=0x80000000 +#define VALIDATE_RANKS mCurrentSize&=0x7fffffff +#define CURRENT_SIZE (mCurrentSize&0x7fffffff) +#define INVALID_RANKS (mCurrentSize&0x80000000) + +#define CHECK_RESIZE(n) \ + if(n!=mPreviousSize) \ + { \ + if(n>mCurrentSize) Resize(n); \ + else ResetRanks(); \ + mPreviousSize = n; \ + } + +#define CREATE_HISTOGRAMS(type, buffer) \ + /* Clear counters/histograms */ \ + ZeroMemory(mHistogram, 256*4*sizeof(udword)); \ + \ + /* Prepare to count */ \ + ubyte* p = (ubyte*)input; \ + ubyte* pe = &p[nb*4]; \ + udword* h0= &mHistogram[0]; /* Histogram for first pass (LSB) */ \ + udword* h1= &mHistogram[256]; /* Histogram for second pass */ \ + udword* h2= &mHistogram[512]; /* Histogram for third pass */ \ + udword* h3= &mHistogram[768]; /* Histogram for last pass (MSB) */ \ + \ + bool AlreadySorted = true; /* Optimism... */ \ + \ + if(INVALID_RANKS) \ + { \ + /* Prepare for temporal coherence */ \ + type* Running = (type*)buffer; \ + type PrevVal = *Running; \ + \ + while(p!=pe) \ + { \ + /* Read input buffer in previous sorted order */ \ + type Val = *Running++; \ + /* Check whether already sorted or not */ \ + if(ValCurSize) Resize(nb); + mCurrentSize = nb; + INVALIDATE_RANKS; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Main sort routine. + * This one is for integer values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data. + * \param input [in] a list of integer values to sort + * \param nb [in] number of values to sort, must be < 2^31 + * \param hint [in] RADIX_SIGNED to handle negative values, RADIX_UNSIGNED if you know your input buffer only contains positive values + * \return Self-Reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint) +{ + // Checkings + if(!input || !nb || nb&0x80000000) return *this; + + // Stats + mTotalCalls++; + + // Resize lists if needed + CheckResize(nb); + +#ifdef RADIX_LOCAL_RAM + // Allocate histograms & offsets on the stack + udword mHistogram[256*4]; +// udword mOffset[256]; + udword* mLink[256]; +#endif + + // Create histograms (counters). Counters for all passes are created in one run. + // Pros: read input buffer once instead of four times + // Cons: mHistogram is 4Kb instead of 1Kb + // We must take care of signed/unsigned values for temporal coherence.... I just + // have 2 code paths even if just a single opcode changes. Self-modifying code, someone? + if(hint==RADIX_UNSIGNED) { CREATE_HISTOGRAMS(udword, input); } + else { CREATE_HISTOGRAMS(sdword, input); } + + // Compute #negative values involved if needed + udword NbNegativeValues = 0; + if(hint==RADIX_SIGNED) + { + // An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128 + // last values of the last histogram. Last histogram because that's the one for the Most Significant Byte, + // responsible for the sign. 128 last values because the 128 first ones are related to positive numbers. + udword* h3= &mHistogram[768]; + for(udword i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part + } + + // Radix sort, j is the pass number (0=LSB, 3=MSB) + for(udword j=0;j<4;j++) + { + CHECK_PASS_VALIDITY(j); + + // Sometimes the fourth (negative) pass is skipped because all numbers are negative and the MSB is 0xFF (for example). This is + // not a problem, numbers are correctly sorted anyway. + if(PerformPass) + { + // Should we care about negative values? + if(j!=3 || hint==RADIX_UNSIGNED) + { + // Here we deal with positive values only + + // Create offsets +// mOffset[0] = 0; +// for(udword i=1;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; + mLink[0] = mRanks2; + for(udword i=1;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; + } + else + { + // This is a special case to correctly handle negative integers. They're sorted in the right order but at the wrong place. + + // Create biased offsets, in order for negative numbers to be sorted as well +// mOffset[0] = NbNegativeValues; // First positive number takes place after the negative ones + mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones +// for(udword i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers + for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers + + // Fixing the wrong place for negative values +// mOffset[128] = 0; + mLink[128] = mRanks2; +// for(i=129;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; + for(udword i=129;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; + } + + // Perform Radix Sort + ubyte* InputBytes = (ubyte*)input; + InputBytes += j; + if(INVALID_RANKS) + { +// for(udword i=0;i>24; // Radix byte, same as above. AND is useless here (udword). + // ### cmp to be killed. Not good. Later. +// if(Radix<128) mRanks2[mOffset[Radix]++] = i; // Number is positive, same as above +// else mRanks2[--mOffset[Radix]] = i; // Number is negative, flip the sorting order + if(Radix<128) *mLink[Radix]++ = i; // Number is positive, same as above + else *(--mLink[Radix]) = i; // Number is negative, flip the sorting order + } + VALIDATE_RANKS; + } + else + { + for(udword i=0;i>24; // Radix byte, same as above. AND is useless here (udword). + // ### cmp to be killed. Not good. Later. +// if(Radix<128) mRanks2[mOffset[Radix]++] = mRanks[i]; // Number is positive, same as above +// else mRanks2[--mOffset[Radix]] = mRanks[i]; // Number is negative, flip the sorting order + if(Radix<128) *mLink[Radix]++ = mRanks[i]; // Number is positive, same as above + else *(--mLink[Radix]) = mRanks[i]; // Number is negative, flip the sorting order + } + } + // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap. + udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp; + } + else + { + // The pass is useless, yet we still have to reverse the order of current list if all values are negative. + if(UniqueVal>=128) + { + if(INVALID_RANKS) + { + // ###Possible? + for(udword i=0;i (b) ? (a) : (b)) //!< Returns the max value between a and b + #define MAXMAX(a,b,c) ((a) > (b) ? MAX (a,c) : MAX (b,c)) //!< Returns the max value between a, b and c + + template inline_ const T& TMin (const T& a, const T& b) { return b < a ? b : a; } + template inline_ const T& TMax (const T& a, const T& b) { return a < b ? b : a; } + template inline_ void TSetMin (T& a, const T& b) { if(a>b) a = b; } + template inline_ void TSetMax (T& a, const T& b) { if(a> 1) & 0x55555555) | ((n << 1) & 0xaaaaaaaa); + n = ((n >> 2) & 0x33333333) | ((n << 2) & 0xcccccccc); + n = ((n >> 4) & 0x0f0f0f0f) | ((n << 4) & 0xf0f0f0f0); + n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00); + n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000); + // Etc for larger intergers (64 bits in Java) + // NOTE: the >> operation must be unsigned! (>>> in java) + } + + //! Count the number of '1' bits in a 32 bit word (from Steve Baker's Cute Code Collection) + inline_ udword CountBits(udword n) + { + // This relies of the fact that the count of n bits can NOT overflow + // an n bit interger. EG: 1 bit count takes a 1 bit interger, 2 bit counts + // 2 bit interger, 3 bit count requires only a 2 bit interger. + // So we add all bit pairs, then each nible, then each byte etc... + n = (n & 0x55555555) + ((n & 0xaaaaaaaa) >> 1); + n = (n & 0x33333333) + ((n & 0xcccccccc) >> 2); + n = (n & 0x0f0f0f0f) + ((n & 0xf0f0f0f0) >> 4); + n = (n & 0x00ff00ff) + ((n & 0xff00ff00) >> 8); + n = (n & 0x0000ffff) + ((n & 0xffff0000) >> 16); + // Etc for larger intergers (64 bits in Java) + // NOTE: the >> operation must be unsigned! (>>> in java) + return n; + } + + //! Even faster? + inline_ udword CountBits2(udword bits) + { + bits = bits - ((bits >> 1) & 0x55555555); + bits = ((bits >> 2) & 0x33333333) + (bits & 0x33333333); + bits = ((bits >> 4) + bits) & 0x0F0F0F0F; + return (bits * 0x01010101) >> 24; + } + + //! Spread out bits. EG 00001111 -> 0101010101 + //! 00001010 -> 0100010000 + //! This is used to interleve to intergers to produce a `Morten Key' + //! used in Space Filling Curves (See DrDobbs Journal, July 1999) + //! Order is important. + inline_ void SpreadBits(udword& n) + { + n = ( n & 0x0000ffff) | (( n & 0xffff0000) << 16); + n = ( n & 0x000000ff) | (( n & 0x0000ff00) << 8); + n = ( n & 0x000f000f) | (( n & 0x00f000f0) << 4); + n = ( n & 0x03030303) | (( n & 0x0c0c0c0c) << 2); + n = ( n & 0x11111111) | (( n & 0x22222222) << 1); + } + + // Next Largest Power of 2 + // Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm + // that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with + // the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next + // largest power of 2. For a 32-bit value: + inline_ udword nlpo2(udword x) + { + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return x+1; + } + + //! Test to see if a number is an exact power of two (from Steve Baker's Cute Code Collection) + inline_ bool IsPowerOfTwo(udword n) { return ((n&(n-1))==0); } + + //! Zero the least significant '1' bit in a word. (from Steve Baker's Cute Code Collection) + inline_ void ZeroLeastSetBit(udword& n) { n&=(n-1); } + + //! Set the least significant N bits in a word. (from Steve Baker's Cute Code Collection) + inline_ void SetLeastNBits(udword& x, udword n) { x|=~(~0<> 31; return (x^y)-y; } + + //!< Alternative min function + inline_ sdword min_(sdword a, sdword b) { sdword delta = b-a; return a + (delta&(delta>>31)); } + + // Determine if one of the bytes in a 4 byte word is zero + inline_ BOOL HasNullByte(udword x) { return ((x + 0xfefefeff) & (~x) & 0x80808080); } + + // To find the smallest 1 bit in a word EG: ~~~~~~10---0 => 0----010---0 + inline_ udword LowestOneBit(udword w) { return ((w) & (~(w)+1)); } +// inline_ udword LowestOneBit_(udword w) { return ((w) & (-(w))); } + + // Most Significant 1 Bit + // Given a binary integer value x, the most significant 1 bit (highest numbered element of a bit set) + // can be computed using a SWAR algorithm that recursively "folds" the upper bits into the lower bits. + // This process yields a bit vector with the same most significant 1 as x, but all 1's below it. + // Bitwise AND of the original value with the complement of the "folded" value shifted down by one + // yields the most significant bit. For a 32-bit value: + inline_ udword msb32(udword x) + { + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return (x & ~(x >> 1)); + } + + /* + "Just call it repeatedly with various input values and always with the same variable as "memory". + The sharpness determines the degree of filtering, where 0 completely filters out the input, and 1 + does no filtering at all. + + I seem to recall from college that this is called an IIR (Infinite Impulse Response) filter. As opposed + to the more typical FIR (Finite Impulse Response). + + Also, I'd say that you can make more intelligent and interesting filters than this, for example filters + that remove wrong responses from the mouse because it's being moved too fast. You'd want such a filter + to be applied before this one, of course." + + (JCAB on Flipcode) + */ + inline_ float FeedbackFilter(float val, float& memory, float sharpness) + { + ASSERT(sharpness>=0.0f && sharpness<=1.0f && "Invalid sharpness value in feedback filter"); + if(sharpness<0.0f) sharpness = 0.0f; + else if(sharpness>1.0f) sharpness = 1.0f; + return memory = val * sharpness + memory * (1.0f - sharpness); + } + + //! If you can guarantee that your input domain (i.e. value of x) is slightly + //! limited (abs(x) must be < ((1<<31u)-32767)), then you can use the + //! following code to clamp the resulting value into [-32768,+32767] range: + inline_ int ClampToInt16(int x) + { +// ASSERT(abs(x) < (int)((1<<31u)-32767)); + + int delta = 32767 - x; + x += (delta>>31) & delta; + delta = x + 32768; + x -= (delta>>31) & delta; + return x; + } + + // Generic functions + template inline_ void TSwap(Type& a, Type& b) { const Type c = a; a = b; b = c; } + template inline_ Type TClamp(const Type& x, const Type& lo, const Type& hi) { return ((xhi) ? hi : x); } + + template inline_ void TSort(Type& a, Type& b) + { + if(a>b) TSwap(a, b); + } + + template inline_ void TSort(Type& a, Type& b, Type& c) + { + if(a>b) TSwap(a, b); + if(b>c) TSwap(b, c); + if(a>b) TSwap(a, b); + if(b>c) TSwap(b, c); + } + + // Prevent nasty user-manipulations (strategy borrowed from Charles Bloom) +// #define PREVENT_COPY(curclass) void operator = (const curclass& object) { ASSERT(!"Bad use of operator ="); } + // ... actually this is better ! + #define PREVENT_COPY(cur_class) private: cur_class(const cur_class& object); cur_class& operator=(const cur_class& object); + + //! TO BE DOCUMENTED + #define OFFSET_OF(Class, Member) (size_t)&(((Class*)0)->Member) + //! TO BE DOCUMENTED + #define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Returns the alignment of the input address. + * \fn Alignment() + * \param address [in] address to check + * \return the best alignment (e.g. 1 for odd addresses, etc) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + FUNCTION ICECORE_API udword Alignment(udword address); + + #define IS_ALIGNED_2(x) ((x&1)==0) + #define IS_ALIGNED_4(x) ((x&3)==0) + #define IS_ALIGNED_8(x) ((x&7)==0) + + inline_ void _prefetch(void const* ptr) { (void)*(char const volatile *)ptr; } + + // Compute implicit coords from an index: + // The idea is to get back 2D coords from a 1D index. + // For example: + // + // 0 1 2 ... nbu-1 + // nbu nbu+1 i ... + // + // We have i, we're looking for the equivalent (u=2, v=1) location. + // i = u + v*nbu + // <=> i/nbu = u/nbu + v + // Since 0 <= u < nbu, u/nbu = 0 (integer) + // Hence: v = i/nbu + // Then we simply put it back in the original equation to compute u = i - v*nbu + inline_ void Compute2DCoords(udword& u, udword& v, udword i, udword nbu) + { + v = i / nbu; + u = i - (v * nbu); + } + + // In 3D: i = u + v*nbu + w*nbu*nbv + // <=> i/(nbu*nbv) = u/(nbu*nbv) + v/nbv + w + // u/(nbu*nbv) is null since u/nbu was null already. + // v/nbv is null as well for the same reason. + // Hence w = i/(nbu*nbv) + // Then we're left with a 2D problem: i' = i - w*nbu*nbv = u + v*nbu + inline_ void Compute3DCoords(udword& u, udword& v, udword& w, udword i, udword nbu, udword nbu_nbv) + { + w = i / (nbu_nbv); + Compute2DCoords(u, v, i - (w * nbu_nbv), nbu); + } + +#endif // __ICEUTILS_H__ diff --git a/Extras/CDTestFramework/Opcode/OPC_ArraySAP.cpp b/Extras/CDTestFramework/Opcode/OPC_ArraySAP.cpp new file mode 100644 index 000000000..a7b0ee207 --- /dev/null +++ b/Extras/CDTestFramework/Opcode/OPC_ArraySAP.cpp @@ -0,0 +1,1221 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an array-based version of the sweep-and-prune algorithm + * \file OPC_ArraySAP.cpp + * \author Pierre Terdiman + * \date December, 2, 2007 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "StdAfx.h" + +using namespace Opcode; + +//#include "SAP_Utils.h" + +#define INVALID_USER_ID 0xffff + +inline_ void Sort(uword& id0, uword& id1) { if(id0>id1) TSwap(id0, id1); } +inline_ void Sort(uword& id0, uword& id1, const void*& obj0, const void*& obj1) { if(id0>id1) { TSwap(id0, id1); TSwap(obj0, obj1); } } + + + struct Opcode::IAABB : public Allocateable + { + udword mMinX; + udword mMinY; + udword mMinZ; + udword mMaxX; + udword mMaxY; + udword mMaxZ; + + inline_ udword GetMin(udword i) const { return (&mMinX)[i]; } + inline_ udword GetMax(udword i) const { return (&mMaxX)[i]; } + }; + + + +/* + - already sorted for batch create? + - better axis selection batch create +*/ + +//#define USE_WORDS // Use words or dwords for box indices. Words save memory but seriously limit the max number of objects in the SAP. +#define USE_PREFETCH +#define USE_INTEGERS +#define PAIR_USER_DATA +#define USE_OVERLAP_TEST_ON_REMOVES // "Useless" but faster overall because seriously reduces number of calls (from ~10000 to ~3 sometimes!) +#define RELEASE_ON_RESET // Release memory instead of just doing a reset + +#include "OPC_ArraySAP.h" + +//#include "SAP_PairManager.h" +//#include "SAP_PairManager.cpp" + + +inline_ udword Hash(uword id0, uword id1) { return Hash32Bits_1( udword(id0)|(udword(id1)<<16) ); } +inline_ bool DifferentPair(const ASAP_Pair& p, uword id0, uword id1) { return (id0!=p.id0) || (id1!=p.id1); } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +ASAP_PairManager::ASAP_PairManager() : + mHashSize (0), + mMask (0), + mHashTable (null), + mNext (null), + mNbActivePairs (0), + mActivePairs (null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +ASAP_PairManager::~ASAP_PairManager() +{ + Purge(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ASAP_PairManager::Purge() +{ + ICE_FREE(mNext); + ICE_FREE(mActivePairs); + ICE_FREE(mHashTable); + mHashSize = 0; + mMask = 0; + mNbActivePairs = 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const ASAP_Pair* ASAP_PairManager::FindPair(uword id0, uword id1) const +{ + if(!mHashTable) return null; // Nothing has been allocated yet + + // Order the ids + Sort(id0, id1); + + // Compute hash value for this pair + udword HashValue = Hash(id0, id1) & mMask; + + // Look for it in the table + udword Offset = mHashTable[HashValue]; + while(Offset!=INVALID_ID && DifferentPair(mActivePairs[Offset], id0, id1)) + { + ASSERT(mActivePairs[Offset].id0!=INVALID_USER_ID); + Offset = mNext[Offset]; // Better to have a separate array for this + } + if(Offset==INVALID_ID) return null; + ASSERT(Offset the pair is persistent + return &mActivePairs[Offset]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Internal version saving hash computation +inline_ ASAP_Pair* ASAP_PairManager::FindPair(uword id0, uword id1, udword hash_value) const +{ + if(!mHashTable) return null; // Nothing has been allocated yet + + // Look for it in the table + udword Offset = mHashTable[hash_value]; + while(Offset!=INVALID_ID && DifferentPair(mActivePairs[Offset], id0, id1)) + { + ASSERT(mActivePairs[Offset].id0!=INVALID_USER_ID); + Offset = mNext[Offset]; // Better to have a separate array for this + } + if(Offset==INVALID_ID) return null; + ASSERT(Offset the pair is persistent + return &mActivePairs[Offset]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const ASAP_Pair* ASAP_PairManager::AddPair(uword id0, uword id1, const void* object0, const void* object1) +{ + // Order the ids + Sort(id0, id1, object0, object1); + + udword HashValue = Hash(id0, id1) & mMask; + + ASAP_Pair* P = FindPair(id0, id1, HashValue); + if(P) + { + return P; // Persistent pair + } + + // This is a new pair + if(mNbActivePairs >= mHashSize) + { + // Get more entries + mHashSize = NextPowerOfTwo(mNbActivePairs+1); + mMask = mHashSize-1; + + ReallocPairs(); + + // Recompute hash value with new hash size + HashValue = Hash(id0, id1) & mMask; + } + + ASAP_Pair* p = &mActivePairs[mNbActivePairs]; + p->id0 = id0; // ### CMOVs would be nice here + p->id1 = id1; + p->object0 = object0; + p->object1 = object1; + + mNext[mNbActivePairs] = mHashTable[HashValue]; + mHashTable[HashValue] = mNbActivePairs++; + return p; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void ASAP_PairManager::RemovePair(uword id0, uword id1, udword hash_value, udword pair_index) +{ + // Walk the hash table to fix mNext + udword Offset = mHashTable[hash_value]; + ASSERT(Offset!=INVALID_ID); + + udword Previous=INVALID_ID; + while(Offset!=pair_index) + { + Previous = Offset; + Offset = mNext[Offset]; + } + + // Let us go/jump us + if(Previous!=INVALID_ID) + { + ASSERT(mNext[Previous]==pair_index); + mNext[Previous] = mNext[pair_index]; + } + // else we were the first + else mHashTable[hash_value] = mNext[pair_index]; + // we're now free to reuse mNext[PairIndex] without breaking the list + +#ifdef _DEBUG + mNext[pair_index]=INVALID_ID; +#endif + // Invalidate entry + + // Fill holes + if(1) + { + // 1) Remove last pair + const udword LastPairIndex = mNbActivePairs-1; + if(LastPairIndex==pair_index) + { + mNbActivePairs--; + } + else + { + const ASAP_Pair* Last = &mActivePairs[LastPairIndex]; + const udword LastHashValue = Hash(Last->id0, Last->id1) & mMask; + + // Walk the hash table to fix mNext + udword Offset = mHashTable[LastHashValue]; + ASSERT(Offset!=INVALID_ID); + + udword Previous=INVALID_ID; + while(Offset!=LastPairIndex) + { + Previous = Offset; + Offset = mNext[Offset]; + } + + // Let us go/jump us + if(Previous!=INVALID_ID) + { + ASSERT(mNext[Previous]==LastPairIndex); + mNext[Previous] = mNext[LastPairIndex]; + } + // else we were the first + else mHashTable[LastHashValue] = mNext[LastPairIndex]; + // we're now free to reuse mNext[LastPairIndex] without breaking the list + +#ifdef _DEBUG + mNext[LastPairIndex]=INVALID_ID; +#endif + + // Don't invalidate entry since we're going to shrink the array + + // 2) Re-insert in free slot + mActivePairs[pair_index] = mActivePairs[LastPairIndex]; +#ifdef _DEBUG + ASSERT(mNext[pair_index]==INVALID_ID); +#endif + mNext[pair_index] = mHashTable[LastHashValue]; + mHashTable[LastHashValue] = pair_index; + + mNbActivePairs--; + } + } +} + +bool ASAP_PairManager::RemovePair(uword id0, uword id1) +{ + // Order the ids + Sort(id0, id1); + + const udword HashValue = Hash(id0, id1) & mMask; + const ASAP_Pair* P = FindPair(id0, id1, HashValue); + if(!P) return false; + ASSERT(P->id0==id0); + ASSERT(P->id1==id1); + + RemovePair(id0, id1, HashValue, GetPairIndex(P)); + + ShrinkMemory(); + return true; +} + +bool ASAP_PairManager::RemovePairs(const BitArray& array) +{ + udword i=0; + while(i only less efficient but still ok + for(udword i=0;i>2; } + }; + + class Opcode::ASAP_Box : public Allocateable + { + public: + inline_ ASAP_Box() {} + inline_ ~ASAP_Box() {} + + IndexType mMin[3]; + IndexType mMax[3]; + void* mObject; + udword mGUID; + + inline_ ValType GetMaxValue(udword i, const ASAP_EndPoint* base) const + { + return base[mMax[i]].mValue; + } + + inline_ ValType GetMinValue(udword i, const ASAP_EndPoint* base) const + { + return base[mMin[i]].mValue; + } +#ifdef _DEBUG + bool HasBeenInserted() const + { + assert(mMin[0]!=INVALID_INDEX); + assert(mMax[0]!=INVALID_INDEX); + assert(mMin[1]!=INVALID_INDEX); + assert(mMax[1]!=INVALID_INDEX); + assert(mMin[2]!=INVALID_INDEX); + assert(mMax[2]!=INVALID_INDEX); + return true; + } +#endif + }; + +inline_ BOOL Intersect1D_Min(const SAP_AABB& a, const ASAP_Box& b, const ASAP_EndPoint* const base, udword axis) +{ + if(b.GetMaxValue(axis, base) < a.GetMin(axis)) + return FALSE; + return TRUE; +} + +inline_ BOOL Intersect2D(const ASAP_Box& c, const ASAP_Box& b, udword axis1, udword axis2) +{ + if( b.mMax[axis1] < c.mMin[axis1] || c.mMax[axis1] < b.mMin[axis1] + || b.mMax[axis2] < c.mMin[axis2] || c.mMax[axis2] < b.mMin[axis2]) return FALSE; + return TRUE; +} + + +ArraySAP::ArraySAP() +{ + mNbBoxes = 0; + mMaxNbBoxes = 0; + mBoxes = null; + mEndPoints[0] = mEndPoints[1] = mEndPoints[2] = null; + mFirstFree = INVALID_ID; +} + +ArraySAP::~ArraySAP() +{ + mNbBoxes = 0; + mMaxNbBoxes = 0; + DELETEARRAY(mBoxes); + for(udword i=0;i<3;i++) + { + DELETEARRAY(mEndPoints[i]); + } +} + +void ArraySAP::ResizeBoxArray() +{ + const udword NewMaxBoxes = mMaxNbBoxes ? mMaxNbBoxes*2 : 64; + + ASAP_Box* NewBoxes = ICE_NEW_TMP(ASAP_Box)[NewMaxBoxes]; + const udword NbSentinels=2; + ASAP_EndPoint* NewEndPointsX = ICE_NEW_TMP(ASAP_EndPoint)[NewMaxBoxes*2+NbSentinels]; + ASAP_EndPoint* NewEndPointsY = ICE_NEW_TMP(ASAP_EndPoint)[NewMaxBoxes*2+NbSentinels]; + ASAP_EndPoint* NewEndPointsZ = ICE_NEW_TMP(ASAP_EndPoint)[NewMaxBoxes*2+NbSentinels]; + + if(mNbBoxes) + { + CopyMemory(NewBoxes, mBoxes, sizeof(ASAP_Box)*mNbBoxes); + CopyMemory(NewEndPointsX, mEndPoints[0], sizeof(ASAP_EndPoint)*(mNbBoxes*2+NbSentinels)); + CopyMemory(NewEndPointsY, mEndPoints[1], sizeof(ASAP_EndPoint)*(mNbBoxes*2+NbSentinels)); + CopyMemory(NewEndPointsZ, mEndPoints[2], sizeof(ASAP_EndPoint)*(mNbBoxes*2+NbSentinels)); + } + else + { + // Initialize sentinels +#ifdef USE_INTEGERS + const udword Min = EncodeFloat(MIN_FLOAT); + const udword Max = EncodeFloat(MAX_FLOAT); +#else + const float Min = MIN_FLOAT; + const float Max = MAX_FLOAT; +#endif + NewEndPointsX[0].SetData(Min, INVALID_INDEX, FALSE); + NewEndPointsX[1].SetData(Max, INVALID_INDEX, TRUE); + NewEndPointsY[0].SetData(Min, INVALID_INDEX, FALSE); + NewEndPointsY[1].SetData(Max, INVALID_INDEX, TRUE); + NewEndPointsZ[0].SetData(Min, INVALID_INDEX, FALSE); + NewEndPointsZ[1].SetData(Max, INVALID_INDEX, TRUE); + } + DELETEARRAY(mBoxes); + DELETEARRAY(mEndPoints[2]); + DELETEARRAY(mEndPoints[1]); + DELETEARRAY(mEndPoints[0]); + mBoxes = NewBoxes; + mEndPoints[0] = NewEndPointsX; + mEndPoints[1] = NewEndPointsY; + mEndPoints[2] = NewEndPointsZ; + + mMaxNbBoxes = NewMaxBoxes; +} + +inline_ BOOL Intersect(const IAABB& a, const IAABB& b, udword axis) +{ + if(b.GetMax(axis) < a.GetMin(axis) || a.GetMax(axis) < b.GetMin(axis)) return FALSE; + return TRUE; +} + +// ### TODO: the sorts here might be useless, as the values have been sorted already +bool ArraySAP::CompleteBoxPruning2(udword nb, const IAABB* array, const Axes& axes, const CreateData* batched) +{ + // Checkings + if(!nb || !array) return false; + + // Catch axes + const udword Axis0 = axes.mAxis0; + const udword Axis1 = axes.mAxis1; + const udword Axis2 = axes.mAxis2; + + // Allocate some temporary data + udword* PosList = (udword*)ICE_ALLOC_TMP(sizeof(udword)*(nb+1)); + + // 1) Build main list using the primary axis + for(udword i=0;iSort(PosList, nb, RADIX_SIGNED).GetRanks(); + const udword* Sorted = RS->Sort(PosList, nb, RADIX_UNSIGNED).GetRanks(); + + // 3) Prune the list + const udword* const LastSorted = &Sorted[nb]; + const udword* RunningAddress = Sorted; + udword Index0, Index1; + while(RunningAddressmObject, Box1->mObject, Box0->mGUID, Box1->mGUID); + } + } + } + } + } + + ICE_FREE(PosList); + return true; +} + +bool ArraySAP::BipartiteBoxPruning2(udword nb0, const IAABB* array0, udword nb1, const IAABB* array1, const Axes& axes, const CreateData* batched, const udword* box_indices) +{ + // Checkings + if(!nb0 || !array0 || !nb1 || !array1) return false; + + // Catch axes + const udword Axis0 = axes.mAxis0; + const udword Axis1 = axes.mAxis1; + const udword Axis2 = axes.mAxis2; + + // Allocate some temporary data + udword* MinPosList0 = (udword*)ICE_ALLOC_TMP(sizeof(udword)*nb0); + udword* MinPosList1 = (udword*)ICE_ALLOC_TMP(sizeof(udword)*nb1); + + // 1) Build main lists using the primary axis + for(udword i=0;iSort(MinPosList0, nb0, RADIX_UNSIGNED).GetRanks(); + const udword* Sorted1 = RS1->Sort(MinPosList1, nb1, RADIX_UNSIGNED).GetRanks(); + + // 3) Prune the lists + udword Index0, Index1; + + const udword* const LastSorted0 = &Sorted0[nb0]; + const udword* const LastSorted1 = &Sorted1[nb1]; + const udword* RunningAddress0 = Sorted0; + const udword* RunningAddress1 = Sorted1; + + while(RunningAddress1mObject, Box1->mObject, Box0->mGUID, Box1->mGUID); + } + } + } + } + + //// + + while(RunningAddress0mObject, Box1->mObject, Box0->mGUID, Box1->mGUID); + } + } + + } + } + + ICE_FREE(MinPosList0); + ICE_FREE(MinPosList1); + return true; +} + +udword ArraySAP::AddObject(void* object, uword guid, const AABB& box) +{ + assert(!(size_t(object)&3)); // We will use the 2 LSBs + +#ifdef _DEBUG + int a = sizeof(ASAP_Box); // 32 + int b = sizeof(ASAP_EndPoint); // 8 +#endif + + udword BoxIndex; + if(mFirstFree!=INVALID_ID) + { + BoxIndex = mFirstFree; + mFirstFree = mBoxes[BoxIndex].mGUID; + } + else + { + if(mNbBoxes==mMaxNbBoxes) + ResizeBoxArray(); + BoxIndex = mNbBoxes; + } + + ASAP_Box* Box = &mBoxes[BoxIndex]; + // Initialize box + Box->mObject = object; + Box->mGUID = guid; + for(udword i=0;i<3;i++) + { + Box->mMin[i] = INVALID_INDEX; + Box->mMax[i] = INVALID_INDEX; + } + + mNbBoxes++; + + CreateData* CD = (CreateData*)mCreated.Reserve(sizeof(CreateData)/sizeof(udword)); + CD->mHandle = BoxIndex; + CD->mBox = box; + + return BoxIndex; +} + +void ArraySAP::InsertEndPoints(udword axis, const ASAP_EndPoint* end_points, udword nb_endpoints) +{ + ASAP_EndPoint* const BaseEP = mEndPoints[axis]; + + const udword OldSize = mNbBoxes*2 - nb_endpoints; + const udword NewSize = mNbBoxes*2; + + BaseEP[NewSize + 1] = BaseEP[OldSize + 1]; + + sdword WriteIdx = NewSize; + udword CurrInsIdx = 0; + + const ASAP_EndPoint* First = &BaseEP[0]; + const ASAP_EndPoint* Current = &BaseEP[OldSize]; + while(Current>=First) + { + const ASAP_EndPoint& Src = *Current; + const ASAP_EndPoint& Ins = end_points[CurrInsIdx]; + + // We need to make sure we insert maxs before mins to handle exactly equal endpoints correctly + const bool ShouldInsert = Ins.IsMax() ? (Src.mValue <= Ins.mValue) : (Src.mValue < Ins.mValue); + + const ASAP_EndPoint& Moved = ShouldInsert ? Ins : Src; + BaseEP[WriteIdx] = Moved; + mBoxes[Moved.GetOwner()].mMin[axis + Moved.IsMax()] = WriteIdx--; + + if(ShouldInsert) + { + CurrInsIdx++; + if(CurrInsIdx >= nb_endpoints) + break;//we just inserted the last endpoint + } + else + { + Current--; + } + } +} + +void ArraySAP::BatchCreate() +{ + udword NbBatched = mCreated.GetNbEntries(); + if(!NbBatched) return; // Early-exit if no object has been created + NbBatched /= sizeof(CreateData)/sizeof(udword); + const CreateData* Batched = (const CreateData*)mCreated.GetEntries(); + mCreated.Reset(); + + { + const udword NbEndPoints = NbBatched*2; + ASAP_EndPoint* NewEPSorted = ICE_NEW_TMP(ASAP_EndPoint)[NbEndPoints]; + ASAP_EndPoint* Buffer = (ASAP_EndPoint*)ICE_ALLOC_TMP(sizeof(ASAP_EndPoint)*NbEndPoints); + RadixSort RS; + + for(udword Axis=0;Axis<3;Axis++) + { + for(udword i=0;iHasBeenInserted()); + } + for(udword i=0;imMin[0]; + NewBoxes[i].mMaxX = Box->mMax[0]; + NewBoxes[i].mMinY = Box->mMin[1]; + NewBoxes[i].mMaxY = Box->mMax[1]; + NewBoxes[i].mMinZ = Box->mMin[2]; + NewBoxes[i].mMaxZ = Box->mMax[2]; + } + + CompleteBoxPruning2(NbBatched, NewBoxes, Axes(AXES_XZY), Batched); + + // the old boxes are not the first ones in the array + + const udword NbOldBoxes = mNbBoxes - NbBatched; + if(NbOldBoxes) + { + IAABB* OldBoxes = ICE_NEW_TMP(IAABB)[NbOldBoxes]; + udword* OldBoxesIndices = (udword*)ICE_ALLOC_TMP(sizeof(udword)*NbOldBoxes); + udword Offset=0; + udword i=0; + while(imMin[0]; + OldBoxes[i].mMaxX = Box->mMax[0]; + OldBoxes[i].mMinY = Box->mMin[1]; + OldBoxes[i].mMaxY = Box->mMax[1]; + OldBoxes[i].mMinZ = Box->mMin[2]; + OldBoxes[i].mMaxZ = Box->mMax[2]; + Offset++; + i++; + } + assert(i==NbOldBoxes); + + BipartiteBoxPruning2(NbBatched, NewBoxes, NbOldBoxes, OldBoxes, Axes(AXES_XZY), Batched, OldBoxesIndices); + + ICE_FREE(OldBoxesIndices); + DELETEARRAY(OldBoxes); + } + DELETEARRAY(NewBoxes); + } +#ifdef RELEASE_ON_RESET + mCreated.Empty(); +#endif +} + +void ArraySAP::BatchRemove() +{ + udword NbRemoved = mRemoved.GetNbEntries(); + if(!NbRemoved) return; // Early-exit if no object has been removed + const udword* Removed = mRemoved.GetEntries(); + mRemoved.Reset(); + + for(udword Axis=0;Axis<3;Axis++) + { + ASAP_EndPoint* const BaseEP = mEndPoints[Axis]; + udword MinMinIndex = MAX_UDWORD; + for(udword i=0;imMin[Axis]; + assert(MinIndexmMax[Axis]; + assert(MaxIndexmMin[Axis + BaseEP[DestIndex].IsMax()] = DestIndex; + } + } + DestIndex++; + ReadIndex++; + } + } + } + + BitArray BA(65536); + const udword Saved = NbRemoved; + while(NbRemoved--) + { + udword Index = *Removed++; + assert(IndexmGUID < 65536); + BA.SetBit(Object->mGUID); + + Object->mGUID = mFirstFree; + mFirstFree = Index; + } + mNbBoxes -= Saved; + mPairs.RemovePairs(BA); + +#ifdef RELEASE_ON_RESET + mRemoved.Empty(); +#endif +} + +bool ArraySAP::RemoveObject(udword handle) +{ + mRemoved.Add(handle); + return true; +} + +#ifdef USE_INTEGERS +bool ArraySAP::UpdateObject(udword handle, const AABB& box_) +#else +bool ArraySAP::UpdateObject(udword handle, const AABB& box) +#endif +{ + const ASAP_Box* Object = mBoxes + handle; + assert(Object->HasBeenInserted()); + const void* UserObject = Object->mObject; + const udword UserGUID = Object->mGUID; + +#ifdef USE_INTEGERS + IAABB box; + box.mMinX = EncodeFloat(box_.GetMin(0)); + box.mMinY = EncodeFloat(box_.GetMin(1)); + box.mMinZ = EncodeFloat(box_.GetMin(2)); + box.mMaxX = EncodeFloat(box_.GetMax(0)); + box.mMaxY = EncodeFloat(box_.GetMax(1)); + box.mMaxZ = EncodeFloat(box_.GetMax(2)); +#endif + + for(udword Axis=0;Axis<3;Axis++) + { + const udword Axis1 = (1 << Axis) & 3; + const udword Axis2 = (1 << Axis1) & 3; + + ASAP_EndPoint* const BaseEP = mEndPoints[Axis]; + + // Update min + { + ASAP_EndPoint* CurrentMin = BaseEP + Object->mMin[Axis]; + ASSERT(!CurrentMin->IsMax()); + + const ValType Limit = box.GetMin(Axis); + if(Limit < CurrentMin->mValue) + { + CurrentMin->mValue = Limit; + + // Min is moving left: + ASAP_EndPoint Saved = *CurrentMin; + udword EPIndex = (size_t(CurrentMin) - size_t(BaseEP))/sizeof(ASAP_EndPoint); + const udword SavedIndex = EPIndex; + + while((--CurrentMin)->mValue > Limit) + { +#ifdef USE_PREFETCH + _prefetch(CurrentMin-1); +#endif + ASAP_Box* id1 = mBoxes + CurrentMin->GetOwner(); + const BOOL IsMax = CurrentMin->IsMax(); + if(IsMax) + { + // Our min passed a max => start overlap + if(Object!=id1 + && Intersect2D(*Object, *id1, Axis1, Axis2) + && Intersect1D_Min(box, *id1, BaseEP, Axis) + ) + AddPair(UserObject, id1->mObject, UserGUID, id1->mGUID); + } + + id1->mMin[Axis + IsMax] = EPIndex--; + *(CurrentMin+1) = *CurrentMin; + } + + if(SavedIndex!=EPIndex) + { + mBoxes[Saved.GetOwner()].mMin[Axis + Saved.IsMax()] = EPIndex; + BaseEP[EPIndex] = Saved; + } + } + else if(Limit > CurrentMin->mValue) + { + CurrentMin->mValue = Limit; + + // Min is moving right: + ASAP_EndPoint Saved = *CurrentMin; + udword EPIndex = (size_t(CurrentMin) - size_t(BaseEP))/sizeof(ASAP_EndPoint); + const udword SavedIndex = EPIndex; + + while((++CurrentMin)->mValue < Limit) + { +#ifdef USE_PREFETCH + _prefetch(CurrentMin+1); +#endif + ASAP_Box* id1 = mBoxes + CurrentMin->GetOwner(); + const BOOL IsMax = CurrentMin->IsMax(); + if(IsMax) + { + // Our min passed a max => stop overlap + if(Object!=id1 +#ifdef USE_OVERLAP_TEST_ON_REMOVES + && Intersect2D(*Object, *id1, Axis1, Axis2) +#endif + ) + RemovePair(UserObject, id1->mObject, UserGUID, id1->mGUID); + } + + id1->mMin[Axis + IsMax] = EPIndex++; + *(CurrentMin-1) = *CurrentMin; + } + + if(SavedIndex!=EPIndex) + { + mBoxes[Saved.GetOwner()].mMin[Axis + Saved.IsMax()] = EPIndex; + BaseEP[EPIndex] = Saved; + } + } + } + + // Update max + { + ASAP_EndPoint* CurrentMax = BaseEP + Object->mMax[Axis]; + ASSERT(CurrentMax->IsMax()); + + const ValType Limit = box.GetMax(Axis); + if(Limit > CurrentMax->mValue) + { + CurrentMax->mValue = Limit; + + // Max is moving right: + ASAP_EndPoint Saved = *CurrentMax; + udword EPIndex = (size_t(CurrentMax) - size_t(BaseEP))/sizeof(ASAP_EndPoint); + const udword SavedIndex = EPIndex; + + while((++CurrentMax)->mValue < Limit) + { +#ifdef USE_PREFETCH + _prefetch(CurrentMax+1); +#endif + ASAP_Box* id1 = mBoxes + CurrentMax->GetOwner(); + const BOOL IsMax = CurrentMax->IsMax(); + if(!IsMax) + { + // Our max passed a min => start overlap + if(Object!=id1 + && Intersect2D(*Object, *id1, Axis1, Axis2) + && Intersect1D_Min(box, *id1, BaseEP, Axis) + ) + AddPair(UserObject, id1->mObject, UserGUID, id1->mGUID); + } + + id1->mMin[Axis + IsMax] = EPIndex++; + *(CurrentMax-1) = *CurrentMax; + } + + if(SavedIndex!=EPIndex) + { + mBoxes[Saved.GetOwner()].mMin[Axis + Saved.IsMax()] = EPIndex; + BaseEP[EPIndex] = Saved; + } + } + else if(Limit < CurrentMax->mValue) + { + CurrentMax->mValue = Limit; + + // Max is moving left: + ASAP_EndPoint Saved = *CurrentMax; + udword EPIndex = (size_t(CurrentMax) - size_t(BaseEP))/sizeof(ASAP_EndPoint); + const udword SavedIndex = EPIndex; + + while((--CurrentMax)->mValue > Limit) + { +#ifdef USE_PREFETCH + _prefetch(CurrentMax-1); +#endif + ASAP_Box* id1 = mBoxes + CurrentMax->GetOwner(); + const BOOL IsMax = CurrentMax->IsMax(); + if(!IsMax) + { + // Our max passed a min => stop overlap + if(Object!=id1 +#ifdef USE_OVERLAP_TEST_ON_REMOVES + && Intersect2D(*Object, *id1, Axis1, Axis2) +#endif + ) + RemovePair(UserObject, id1->mObject, UserGUID, id1->mGUID); + } + + id1->mMin[Axis + IsMax] = EPIndex--; + *(CurrentMax+1) = *CurrentMax; + } + + if(SavedIndex!=EPIndex) + { + mBoxes[Saved.GetOwner()].mMin[Axis + Saved.IsMax()] = EPIndex; + BaseEP[EPIndex] = Saved; + } + } + } + } + return true; +} + +udword ArraySAP::DumpPairs(SAP_CreatePair create_cb, SAP_DeletePair delete_cb, void* cb_user_data, ASAP_Pair** pairs) +{ + BatchCreate(); + + const udword* Entries = mData.GetEntries(); + const udword* Last = Entries + mData.GetNbEntries(); + mData.Reset(); + + udword* ToRemove = (udword*)Entries; + while(Entries!=Last) + { + const udword ID = *Entries++; + ASAP_Pair* UP = mPairs.mActivePairs + ID; + + { + ASSERT(UP->IsInArray()); + if(UP->IsRemoved()) + { + // No need to call "ClearInArray" in this case, since the pair will get removed anyway + + // Remove + if(delete_cb && !UP->IsNew()) + { +#ifdef PAIR_USER_DATA + (delete_cb)(UP->GetObject0(), UP->GetObject1(), cb_user_data, UP->userData); +#else + (delete_cb)(UP->GetObject0(), UP->GetObject1(), cb_user_data, null); +#endif + } + + *ToRemove++ = udword(UP->id0)<<16|UP->id1; + } + else + { + UP->ClearInArray(); + // Add => already there... Might want to create user data, though + if(UP->IsNew()) + { + if(create_cb) + { +#ifdef PAIR_USER_DATA + UP->userData = (create_cb)(UP->GetObject0(), UP->GetObject1(), cb_user_data); +#else + (create_cb)(UP->GetObject0(), UP->GetObject1(), cb_user_data); +#endif + } + UP->ClearNew(); + } + } + } + } + + // #### try batch removal here + Entries = mData.GetEntries(); + while(Entries!=ToRemove) + { + const udword ID = *Entries++; + const udword id0 = ID>>16; + const udword id1 = ID&0xffff; + bool Status = mPairs.RemovePair(id0, id1); + ASSERT(Status); + } + +#ifdef RELEASE_ON_RESET + mData.Empty(); +#endif + + BatchRemove(); + + if(pairs) *pairs = mPairs.mActivePairs; + + return mPairs.mNbActivePairs; +} diff --git a/Extras/CDTestFramework/Opcode/OPC_ArraySAP.h b/Extras/CDTestFramework/Opcode/OPC_ArraySAP.h new file mode 100644 index 000000000..f9d370667 --- /dev/null +++ b/Extras/CDTestFramework/Opcode/OPC_ArraySAP.h @@ -0,0 +1,159 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an array-based version of the sweep-and-prune algorithm + * \file OPC_ArraySAP.h + * \author Pierre Terdiman + * \date December, 2, 2007 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef OPC_ARRAYSAP_H +#define OPC_ARRAYSAP_H + +#pragma pack(1) + struct OPCODE_API ASAP_Pair + { + uword id0; + uword id1; + const void* object0; + const void* object1; +//#ifdef PAIR_USER_DATA + void* userData; +//#endif + inline_ const void* GetObject0() const { return (const void*)(size_t(object0) & ~3); } + inline_ const void* GetObject1() const { return (const void*)(size_t(object1) & ~3); } + inline_ size_t IsInArray() const { return size_t(object0) & 1; } + inline_ size_t IsRemoved() const { return size_t(object1) & 1; } + inline_ size_t IsNew() const { return size_t(object0) & 2; } + private: + inline_ void SetInArray() { object0 = (const void*)(size_t(object0) | 1); } + inline_ void SetRemoved() { object1 = (const void*)(size_t(object1) | 1); } + inline_ void SetNew() { object0 = (const void*)(size_t(object0) | 2); } + inline_ void ClearInArray() { object0 = (const void*)(size_t(object0) & ~1); } + inline_ void ClearRemoved() { object1 = (const void*)(size_t(object1) & ~1); } + inline_ void ClearNew() { object0 = (const void*)(size_t(object0) & ~2); } + + friend class ArraySAP; + }; +#pragma pack() + + class OPCODE_API ASAP_PairManager + { + public: + ASAP_PairManager(); + ~ASAP_PairManager(); + + void Purge(); + void ShrinkMemory(); + + const ASAP_Pair* AddPair (uword id0, uword id1, const void* object0, const void* object1); + bool RemovePair (uword id0, uword id1); + bool RemovePairs (const BitArray& array); + const ASAP_Pair* FindPair (uword id0, uword id1) const; + inline_ udword GetPairIndex(const ASAP_Pair* pair) const + { + return ((udword)((size_t(pair) - size_t(mActivePairs)))/sizeof(ASAP_Pair)); + } + + udword mHashSize; + udword mMask; + udword mNbActivePairs; + udword* mHashTable; + udword* mNext; + ASAP_Pair* mActivePairs; + inline_ ASAP_Pair* FindPair(uword id0, uword id1, udword hash_value) const; + void RemovePair(uword id0, uword id1, udword hash_value, udword pair_index); + void ReallocPairs(); + }; + + typedef void* (*SAP_CreatePair)(const void* object0, const void* object1, void* user_data); + typedef void (*SAP_DeletePair)(const void* object0, const void* object1, void* user_data, void* pair_user_data); + + // Forward declarations + class ASAP_EndPoint; + class ASAP_Box; + struct IAABB; + struct CreateData; + + class OPCODE_API ArraySAP : public Allocateable + { + public: + ArraySAP(); + ~ArraySAP(); + + udword AddObject(void* object, uword guid, const AABB& box); + bool RemoveObject(udword handle); + bool UpdateObject(udword handle, const AABB& box); + + udword DumpPairs(SAP_CreatePair create_cb, SAP_DeletePair delete_cb, void* cb_user_data, ASAP_Pair** pairs=null); + private: + Container mData; + ASAP_PairManager mPairs; + + inline_ void AddPair(const void* object0, const void* object1, uword id0, uword id1) + { + ASSERT(object0); + ASAP_Pair* UP = (ASAP_Pair*)mPairs.AddPair(id0, id1, null, null); + ASSERT(UP); + + if(UP->object0) + { + // Persistent pair + } + else + { + // New pair + ASSERT(!(int(object0)&1)); + ASSERT(!(int(object1)&1)); + UP->object0 = object0; + UP->object1 = object1; + UP->SetInArray(); + mData.Add(mPairs.GetPairIndex(UP)); + UP->SetNew(); + } + UP->ClearRemoved(); + } + + inline_ void RemovePair(const void* object0, const void* object1, uword id0, uword id1) + { + ASAP_Pair* UP = (ASAP_Pair*)mPairs.FindPair(id0, id1); + if(UP) + { + if(!UP->IsInArray()) + { + UP->SetInArray(); + mData.Add(mPairs.GetPairIndex(UP)); + } + UP->SetRemoved(); + } + } + + udword mNbBoxes; + udword mMaxNbBoxes; + ASAP_Box* mBoxes; + ASAP_EndPoint* mEndPoints[3]; + udword mFirstFree; + + void ResizeBoxArray(); + // For batch creation + Container mCreated; + void BatchCreate(); + void InsertEndPoints(udword axis, const ASAP_EndPoint* end_points, udword nb_endpoints); + bool CompleteBoxPruning2(udword nb, const IAABB* array, const Axes& axes, const CreateData* batched); + bool BipartiteBoxPruning2(udword nb0, const IAABB* array0, udword nb1, const IAABB* array1, const Axes& axes, const CreateData* batched, const udword* box_indices); + // For batch removal + Container mRemoved; + void BatchRemove(); + }; + +#endif // OPC_ARRAYSAP_H diff --git a/Extras/CDTestFramework/Opcode/OPC_BoxPruning.cpp b/Extras/CDTestFramework/Opcode/OPC_BoxPruning.cpp index f9a4cbba1..bd88526cc 100644 --- a/Extras/CDTestFramework/Opcode/OPC_BoxPruning.cpp +++ b/Extras/CDTestFramework/Opcode/OPC_BoxPruning.cpp @@ -67,17 +67,17 @@ static PRUNING_SORTER* gBipartitePruningSorter0 = null; static PRUNING_SORTER* gBipartitePruningSorter1 = null; inline_ PRUNING_SORTER* GetCompletePruningSorter() { - if(!gCompletePruningSorter) gCompletePruningSorter = new PRUNING_SORTER; + if(!gCompletePruningSorter) gCompletePruningSorter = ICE_NEW(PRUNING_SORTER); return gCompletePruningSorter; } inline_ PRUNING_SORTER* GetBipartitePruningSorter0() { - if(!gBipartitePruningSorter0) gBipartitePruningSorter0 = new PRUNING_SORTER; + if(!gBipartitePruningSorter0) gBipartitePruningSorter0 = ICE_NEW(PRUNING_SORTER); return gBipartitePruningSorter0; } inline_ PRUNING_SORTER* GetBipartitePruningSorter1() { - if(!gBipartitePruningSorter1) gBipartitePruningSorter1 = new PRUNING_SORTER; + if(!gBipartitePruningSorter1) gBipartitePruningSorter1 = ICE_NEW(PRUNING_SORTER); return gBipartitePruningSorter1; } void ReleasePruningSorters() diff --git a/Extras/CDTestFramework/Opcode/OPC_IceHook.h b/Extras/CDTestFramework/Opcode/OPC_IceHook.h index 48d901ffa..ea2586a43 100644 --- a/Extras/CDTestFramework/Opcode/OPC_IceHook.h +++ b/Extras/CDTestFramework/Opcode/OPC_IceHook.h @@ -56,11 +56,14 @@ subject to the following restrictions: namespace IceCore { + #include ".\Ice\IceAllocator.h" #include ".\Ice\IceUtils.h" + #include ".\Ice\IceBitArray.h" #include ".\Ice\IceContainer.h" #include ".\Ice\IcePairs.h" #include ".\Ice\IceRevisitedRadix.h" #include ".\Ice\IceRandom.h" + #include ".\Ice\IceHashing.h" } using namespace IceCore; diff --git a/Extras/CDTestFramework/Opcode/Opcode.h b/Extras/CDTestFramework/Opcode/Opcode.h index bb666917f..c0cd1c53f 100644 --- a/Extras/CDTestFramework/Opcode/Opcode.h +++ b/Extras/CDTestFramework/Opcode/Opcode.h @@ -90,6 +90,7 @@ subject to the following restrictions: // Sweep-and-prune #include "OPC_BoxPruning.h" #include "OPC_SweepAndPrune.h" + #include "OPC_ArraySAP.h" FUNCTION OPCODE_API bool InitOpcode(); FUNCTION OPCODE_API bool CloseOpcode(); diff --git a/Extras/CDTestFramework/Opcode/Opcode.vcproj b/Extras/CDTestFramework/Opcode/Opcode.vcproj index 00413fdd9..04838f37f 100644 --- a/Extras/CDTestFramework/Opcode/Opcode.vcproj +++ b/Extras/CDTestFramework/Opcode/Opcode.vcproj @@ -1,7 +1,7 @@ @@ -825,6 +825,14 @@ + + + + @@ -945,10 +953,30 @@ RelativePath="Ice\IceAABB.h" > + + + + + + + + + + @@ -985,6 +1013,10 @@ RelativePath="Ice\IceFPU.h" > + + diff --git a/Extras/CDTestFramework/OpcodeArraySAPTest.cpp b/Extras/CDTestFramework/OpcodeArraySAPTest.cpp new file mode 100644 index 000000000..4e5fede7c --- /dev/null +++ b/Extras/CDTestFramework/OpcodeArraySAPTest.cpp @@ -0,0 +1,210 @@ +/* +CDTestFramework http://codercorner.com +Copyright (c) 2007-2008 Pierre Terdiman, pierre@codercorner.com + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#include "stdafx.h" +#include "OpcodeArraySAPTest.h" +#include "RenderingHelpers.h" +#include "GLFontRenderer.h" + +static udword gNbCreatedPairs; +static udword gNbDeletedPairs; +static udword gTotalNbPairs; + +static void* CBData = (void*)0x12345678; +static void* PairUserData = (void*)0xDeadDead; + +static void* CreatePairCB(const void* object0, const void* object1, void* user_data) +{ + assert(user_data==CBData); + + gNbCreatedPairs++; + return PairUserData; +} + +static void DeletePairCB(const void* object0, const void* object1, void* user_data, void* pair_user_data) +{ + assert(user_data==CBData); + assert(pair_user_data==PairUserData); + + gNbDeletedPairs++; +} + +OpcodeArraySAPTest::OpcodeArraySAPTest(int numBoxes) : + mBar (null), + mNbBoxes (numBoxes), + mBoxes (null), + mHandles (null), + mBoxTime (null), + mSpeed (0.005f), + mAmplitude (100.0f) +{ +} + +OpcodeArraySAPTest::~OpcodeArraySAPTest() +{ + Release(); +} + +void OpcodeArraySAPTest::Init() +{ + m_firstTime = true; + + SRand(0); + mBoxes = new AABB[mNbBoxes]; + mBoxTime = new float[mNbBoxes]; + mHandles = new void*[mNbBoxes]; + for(udword i=0;i