// Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of NVIDIA CORPORATION nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright (c) 2008-2023 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_BITMAP_H #define PX_BITMAP_H #include "foundation/PxAssert.h" #include "foundation/PxMath.h" #include "foundation/PxMemory.h" #include "foundation/PxAllocator.h" #include "foundation/PxUserAllocated.h" #include "foundation/PxIntrinsics.h" #include "foundation/PxBitUtils.h" #if !PX_DOXYGEN namespace physx { #endif /*! Hold a bitmap with operations to set,reset or test given bit. We inhibit copy to prevent unintentional copies. If a copy is desired copy() should be used or alternatively a copy constructor implemented. */ template class PxBitMapBase : public PxUserAllocated { //= ATTENTION! ===================================================================================== // Changing the data layout of this class breaks the binary serialization format. See comments for // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION // accordingly. //================================================================================================== PX_NOCOPY(PxBitMapBase) public: // PX_SERIALIZATION /* todo: explicit */ PxBitMapBase(const PxEMPTY) { if (mMap) mWordCount |= PX_SIGN_BITMASK; } //~PX_SERIALIZATION PX_INLINE PxBitMapBase(const PxAllocator& allocator) : mMap(0), mWordCount(0), mAllocator(allocator) {} PX_INLINE PxBitMapBase() : mMap(0), mWordCount(0) {} PX_INLINE ~PxBitMapBase() { release(); } PX_INLINE void release() { if (mMap && !isInUserMemory()) mAllocator.deallocate(mMap); mMap = NULL; } PX_INLINE PxAllocator& getAllocator() { return mAllocator; } PX_INLINE void growAndSet(PxU32 index) { extend(index + 1); mMap[index >> 5] |= 1 << (index & 31); } PX_INLINE void growAndReset(PxU32 index) { extend(index + 1); mMap[index >> 5] &= ~(1 << (index & 31)); } PX_INLINE PxIntBool boundedTest(PxU32 index) const { return PxIntBool(index >> 5 >= getWordCount() ? PxIntFalse : (mMap[index >> 5] & (1 << (index & 31)))); } PX_INLINE void boundedReset(PxU32 index) { if((index >> 5) < getWordCount()) mMap[index >> 5] &= ~(1 << (index & 31)); } // Special optimized versions, when you _know_ your index is in range PX_INLINE void set(PxU32 index) { PX_ASSERT(index> 5] |= 1 << (index & 31); } PX_INLINE void reset(PxU32 index) { PX_ASSERT(index> 5] &= ~(1 << (index & 31)); } PX_INLINE PxIntBool test(PxU32 index) const { PX_ASSERT(index> 5] & (1 << (index & 31))); } // nibble == 4 bits PX_INLINE PxU32 getNibbleFast(PxU32 nibIndex) const { const PxU32 bitIndex = nibIndex << 2; PX_ASSERT(bitIndex < getWordCount() * 32); return (mMap[bitIndex >> 5] >> (bitIndex & 31)) & 0xf; } PX_INLINE void andNibbleFast(PxU32 nibIndex, PxU32 mask) { //TODO: there has to be a faster way... const PxU32 bitIndex = nibIndex << 2; const PxU32 shift = (bitIndex & 31); const PxU32 nibMask = (0xfu << shift); PX_ASSERT(bitIndex < getWordCount() * 32); mMap[bitIndex >> 5] &= ((mask << shift) | ~nibMask); } PX_INLINE void orNibbleFast(PxU32 nibIndex, PxU32 mask) { PX_ASSERT(!(mask & ~0xfu)); //check extra bits are not set const PxU32 bitIndex = nibIndex << 2; const PxU32 shift = bitIndex & 31; PX_ASSERT(bitIndex < getWordCount() * 32); mMap[bitIndex >> 5] |= (mask << shift); } void clear() { PxMemSet(mMap, 0, getWordCount() * sizeof(PxU32)); } void resizeAndClear(PxU32 newBitCount) { extendUninitialized(newBitCount); PxMemSet(mMap, 0, getWordCount() * sizeof(PxU32)); } void setEmpty() { mMap = NULL; mWordCount = 0; } void setWords(PxU32* map, PxU32 wordCount) { mMap = map; mWordCount = wordCount; mWordCount |= PX_SIGN_BITMASK; } // !!! only sets /last/ bit to value void resize(PxU32 newBitCount, bool value = false) { PX_ASSERT(!value); // only new class supports this PX_UNUSED(value); extend(newBitCount); } PxU32 size() const { return getWordCount() * 32; } void copy(const PxBitMapBase& a) { extendUninitialized(a.getWordCount() << 5); PxMemCopy(mMap, a.mMap, a.getWordCount() * sizeof(PxU32)); if (getWordCount() > a.getWordCount()) PxMemSet(mMap + a.getWordCount(), 0, (getWordCount() - a.getWordCount()) * sizeof(PxU32)); } PX_INLINE PxU32 count() const { // NOTE: we can probably do this faster, since the last steps in PxcBitCount32 can be defered to // the end of the seq. + 64/128bits at a time + native bit counting instructions(360 is fast non micro code). PxU32 count = 0; const PxU32 wordCount = getWordCount(); for (PxU32 i = 0; i 0;) { if (mMap[i]) return (i << 5) + PxHighestSetBit(mMap[i]); } return PxU32(0); } // the obvious combiners and some used in the SDK struct OR { PX_INLINE PxU32 operator()(PxU32 a, PxU32 b) { return a | b; } }; struct AND { PX_INLINE PxU32 operator()(PxU32 a, PxU32 b) { return a&b; } }; struct XOR { PX_INLINE PxU32 operator()(PxU32 a, PxU32 b) { return a^b; } }; // we use auxiliary functions here so as not to generate combiners for every combination // of allocators template PX_INLINE void combineInPlace(const PxBitMapBase<_>& b) { combine1(b.mMap, b.getWordCount()); } template PX_INLINE void combine(const PxBitMapBase<_1>& a, const PxBitMapBase<_2>& b) { combine2(a.mMap, a.getWordCount(), b.mMap, b.getWordCount()); } PX_FORCE_INLINE const PxU32* getWords() const { return mMap; } PX_FORCE_INLINE PxU32* getWords() { return mMap; } // PX_SERIALIZATION PX_FORCE_INLINE PxU32 getWordCount() const { return mWordCount & ~PX_SIGN_BITMASK; } // We need one bit to mark arrays that have been deserialized from a user-provided memory block. PX_FORCE_INLINE PxU32 isInUserMemory() const { return mWordCount & PX_SIGN_BITMASK; } //~PX_SERIALIZATION /*! Iterate over indices in a bitmap This iterator is good because it finds the set bit without looping over the cached bits upto 31 times. However it does require a variable shift. */ class Iterator { public: static const PxU32 DONE = 0xffffffff; PX_INLINE Iterator(const PxBitMapBase &map) : mBitMap(map) { reset(); } PX_INLINE Iterator& operator=(const Iterator& other) { PX_ASSERT(&mBitMap == &other.mBitMap); mBlock = other.mBlock; mIndex = other.mIndex; return *this; } PX_INLINE PxU32 getNext() { if (mBlock) { PxU32 bitIndex = mIndex << 5 | PxLowestSetBit(mBlock); mBlock &= mBlock - 1; PxU32 wordCount = mBitMap.getWordCount(); while (!mBlock && ++mIndex < wordCount) mBlock = mBitMap.mMap[mIndex]; return bitIndex; } return DONE; } PX_INLINE void reset() { mIndex = mBlock = 0; PxU32 wordCount = mBitMap.getWordCount(); while (mIndex < wordCount && ((mBlock = mBitMap.mMap[mIndex]) == 0)) ++mIndex; } private: PxU32 mBlock, mIndex; const PxBitMapBase& mBitMap; }; // DS: faster but less general: hasBits() must be true or getNext() is illegal so it is the calling code's responsibility to ensure that getNext() is not called illegally. class PxLoopIterator { PX_NOCOPY(PxLoopIterator) public: PX_FORCE_INLINE PxLoopIterator(const PxBitMapBase &map) : mMap(map.getWords()), mBlock(0), mIndex(-1), mWordCount(PxI32(map.getWordCount())) {} PX_FORCE_INLINE bool hasBits() { PX_ASSERT(mIndex> 5; if (newWordCount > getWordCount()) { PxU32* newMap = reinterpret_cast(mAllocator.allocate(newWordCount * sizeof(PxU32), __FILE__, __LINE__)); if (mMap) { PxMemCopy(newMap, mMap, getWordCount() * sizeof(PxU32)); if (!isInUserMemory()) mAllocator.deallocate(mMap); } PxMemSet(newMap + getWordCount(), 0, (newWordCount - getWordCount()) * sizeof(PxU32)); mMap = newMap; // also resets the isInUserMemory bit mWordCount = newWordCount; } } void extendUninitialized(PxU32 size) { PxU32 newWordCount = (size + 31) >> 5; if (newWordCount > getWordCount()) { if (mMap && !isInUserMemory()) mAllocator.deallocate(mMap); // also resets the isInUserMemory bit mWordCount = newWordCount; mMap = reinterpret_cast(mAllocator.allocate(mWordCount * sizeof(PxU32), __FILE__, __LINE__)); } } template void combine1(const PxU32* words, PxU32 length) { extend(length << 5); PxU32 combineLength = PxMin(getWordCount(), length); for (PxU32 i = 0; i void combine2(const PxU32* words1, PxU32 length1, const PxU32* words2, PxU32 length2) { extendUninitialized(PxMax(length1, length2) << 5); PxU32 commonSize = PxMin(length1, length2); for (PxU32 i = 0; i PxBitMap; typedef PxBitMapBase PxBitMapPinned; #if !PX_DOXYGEN } // namespace physx #endif #endif