diff --git a/Extras/CDTestFramework/Bin/Opcode.dll b/Extras/CDTestFramework/Bin/Opcode.dll index edc82f030..bdb17d72c 100644 Binary files a/Extras/CDTestFramework/Bin/Opcode.dll and b/Extras/CDTestFramework/Bin/Opcode.dll differ 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