Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-17 09:20:05

0001 // Created on: 2016-04-13
0002 // Created by: Denis BOGOLEPOV
0003 // Copyright (c) 2013-2016 OPEN CASCADE SAS
0004 //
0005 // This file is part of Open CASCADE Technology software library.
0006 //
0007 // This library is free software; you can redistribute it and/or modify it under
0008 // the terms of the GNU Lesser General Public License version 2.1 as published
0009 // by the Free Software Foundation, with special exception defined in the file
0010 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
0011 // distribution for complete text of the license and disclaimer of any warranty.
0012 //
0013 // Alternatively, this file may be used under the terms of Open CASCADE
0014 // commercial license or contractual agreement.
0015 
0016 #ifndef _BVH_RadixSorter_Header
0017 #define _BVH_RadixSorter_Header
0018 
0019 #include <BVH_Sorter.hxx>
0020 #include <BVH_Builder.hxx>
0021 #include <NCollection_Array1.hxx>
0022 #include <NCollection_Shared.hxx>
0023 #include <OSD_Parallel.hxx>
0024 
0025 #include <algorithm>
0026 
0027 //! Pair of Morton code and primitive ID.
0028 typedef std::pair<unsigned int, int> BVH_EncodedLink;
0029 
0030 namespace BVH
0031 {
0032 //! Lookup table for expanding 8-bit value to 24-bit Morton code component.
0033 //! Each bit is spread to every 3rd position for interleaving with other components.
0034 constexpr unsigned int THE_MORTON_LUT[256] = {
0035   0x000000, 0x000001, 0x000008, 0x000009, 0x000040, 0x000041, 0x000048, 0x000049, 0x000200,
0036   0x000201, 0x000208, 0x000209, 0x000240, 0x000241, 0x000248, 0x000249, 0x001000, 0x001001,
0037   0x001008, 0x001009, 0x001040, 0x001041, 0x001048, 0x001049, 0x001200, 0x001201, 0x001208,
0038   0x001209, 0x001240, 0x001241, 0x001248, 0x001249, 0x008000, 0x008001, 0x008008, 0x008009,
0039   0x008040, 0x008041, 0x008048, 0x008049, 0x008200, 0x008201, 0x008208, 0x008209, 0x008240,
0040   0x008241, 0x008248, 0x008249, 0x009000, 0x009001, 0x009008, 0x009009, 0x009040, 0x009041,
0041   0x009048, 0x009049, 0x009200, 0x009201, 0x009208, 0x009209, 0x009240, 0x009241, 0x009248,
0042   0x009249, 0x040000, 0x040001, 0x040008, 0x040009, 0x040040, 0x040041, 0x040048, 0x040049,
0043   0x040200, 0x040201, 0x040208, 0x040209, 0x040240, 0x040241, 0x040248, 0x040249, 0x041000,
0044   0x041001, 0x041008, 0x041009, 0x041040, 0x041041, 0x041048, 0x041049, 0x041200, 0x041201,
0045   0x041208, 0x041209, 0x041240, 0x041241, 0x041248, 0x041249, 0x048000, 0x048001, 0x048008,
0046   0x048009, 0x048040, 0x048041, 0x048048, 0x048049, 0x048200, 0x048201, 0x048208, 0x048209,
0047   0x048240, 0x048241, 0x048248, 0x048249, 0x049000, 0x049001, 0x049008, 0x049009, 0x049040,
0048   0x049041, 0x049048, 0x049049, 0x049200, 0x049201, 0x049208, 0x049209, 0x049240, 0x049241,
0049   0x049248, 0x049249, 0x200000, 0x200001, 0x200008, 0x200009, 0x200040, 0x200041, 0x200048,
0050   0x200049, 0x200200, 0x200201, 0x200208, 0x200209, 0x200240, 0x200241, 0x200248, 0x200249,
0051   0x201000, 0x201001, 0x201008, 0x201009, 0x201040, 0x201041, 0x201048, 0x201049, 0x201200,
0052   0x201201, 0x201208, 0x201209, 0x201240, 0x201241, 0x201248, 0x201249, 0x208000, 0x208001,
0053   0x208008, 0x208009, 0x208040, 0x208041, 0x208048, 0x208049, 0x208200, 0x208201, 0x208208,
0054   0x208209, 0x208240, 0x208241, 0x208248, 0x208249, 0x209000, 0x209001, 0x209008, 0x209009,
0055   0x209040, 0x209041, 0x209048, 0x209049, 0x209200, 0x209201, 0x209208, 0x209209, 0x209240,
0056   0x209241, 0x209248, 0x209249, 0x240000, 0x240001, 0x240008, 0x240009, 0x240040, 0x240041,
0057   0x240048, 0x240049, 0x240200, 0x240201, 0x240208, 0x240209, 0x240240, 0x240241, 0x240248,
0058   0x240249, 0x241000, 0x241001, 0x241008, 0x241009, 0x241040, 0x241041, 0x241048, 0x241049,
0059   0x241200, 0x241201, 0x241208, 0x241209, 0x241240, 0x241241, 0x241248, 0x241249, 0x248000,
0060   0x248001, 0x248008, 0x248009, 0x248040, 0x248041, 0x248048, 0x248049, 0x248200, 0x248201,
0061   0x248208, 0x248209, 0x248240, 0x248241, 0x248248, 0x248249, 0x249000, 0x249001, 0x249008,
0062   0x249009, 0x249040, 0x249041, 0x249048, 0x249049, 0x249200, 0x249201, 0x249208, 0x249209,
0063   0x249240, 0x249241, 0x249248, 0x249249};
0064 
0065 //! Encodes 10-bit voxel coordinates into 30-bit Morton code using LUT.
0066 //! @param theVoxelX X coordinate (0-1023)
0067 //! @param theVoxelY Y coordinate (0-1023)
0068 //! @param theVoxelZ Z coordinate (0-1023)
0069 //! @return 30-bit Morton code with interleaved bits
0070 constexpr unsigned int EncodeMortonCode(unsigned int theVoxelX,
0071                                         unsigned int theVoxelY,
0072                                         unsigned int theVoxelZ)
0073 {
0074   // Split each 10-bit coordinate into two 8-bit lookups (upper 2 bits + lower 8 bits)
0075   // For 10-bit values, we use lower 8 bits via LUT and handle upper 2 bits separately
0076   return (THE_MORTON_LUT[theVoxelX & 0xFF] | (THE_MORTON_LUT[(theVoxelX >> 8) & 0x03] << 24))
0077          | ((THE_MORTON_LUT[theVoxelY & 0xFF] | (THE_MORTON_LUT[(theVoxelY >> 8) & 0x03] << 24))
0078             << 1)
0079          | ((THE_MORTON_LUT[theVoxelZ & 0xFF] | (THE_MORTON_LUT[(theVoxelZ >> 8) & 0x03] << 24))
0080             << 2);
0081 }
0082 } // namespace BVH
0083 
0084 //! Performs radix sort of a BVH primitive set using
0085 //! 10-bit Morton codes (or 1024 x 1024 x 1024 grid).
0086 template <class T, int N>
0087 class BVH_RadixSorter : public BVH_Sorter<T, N>
0088 {
0089 public:
0090   typedef typename BVH::VectorType<T, N>::Type BVH_VecNt;
0091 
0092 public:
0093   //! Creates new BVH radix sorter for the given AABB.
0094   BVH_RadixSorter(const BVH_Box<T, N>& theBox)
0095       : myBox(theBox)
0096   {
0097   }
0098 
0099   //! Sorts the set.
0100   void Perform(BVH_Set<T, N>* theSet) override { Perform(theSet, 0, theSet->Size() - 1); }
0101 
0102   //! Sorts the given (inclusive) range in the set.
0103   void Perform(BVH_Set<T, N>* theSet, const int theStart, const int theFinal) override;
0104 
0105   //! Returns Morton codes assigned to BVH primitives.
0106   const NCollection_Array1<BVH_EncodedLink>& EncodedLinks() const { return *myEncodedLinks; }
0107 
0108 protected:
0109   //! Axis-aligned bounding box (AABB) to perform sorting.
0110   BVH_Box<T, N> myBox;
0111 
0112   //! Morton codes assigned to BVH primitives.
0113   Handle(NCollection_Shared<NCollection_Array1<BVH_EncodedLink>>) myEncodedLinks;
0114 };
0115 
0116 namespace BVH
0117 {
0118 // Radix sort STL predicate for 32-bit integer.
0119 struct BitPredicate
0120 {
0121   unsigned int myBit;
0122 
0123   //! Creates new radix sort predicate.
0124   BitPredicate(const int theDigit)
0125       : myBit(1U << theDigit)
0126   {
0127   }
0128 
0129   //! Returns predicate value.
0130   bool operator()(const BVH_EncodedLink theLink) const
0131   {
0132     return !(theLink.first & myBit); // 0-bit to the left side
0133   }
0134 };
0135 
0136 //! STL compare tool used in binary search algorithm.
0137 struct BitComparator
0138 {
0139   unsigned int myBit;
0140 
0141   //! Creates new STL comparator.
0142   BitComparator(const int theDigit)
0143       : myBit(1U << theDigit)
0144   {
0145   }
0146 
0147   //! Checks left value for the given bit.
0148   bool operator()(BVH_EncodedLink theLink1, BVH_EncodedLink /*theLink2*/)
0149   {
0150     return !(theLink1.first & myBit);
0151   }
0152 };
0153 
0154 //! Tool object for sorting link array using radix sort algorithm.
0155 class RadixSorter
0156 {
0157 public:
0158   typedef NCollection_Array1<BVH_EncodedLink>::iterator LinkIterator;
0159 
0160 private:
0161   //! Structure defining sorting range.
0162   struct SortRange
0163   {
0164     LinkIterator myStart; //!< Start element of exclusive sorting range
0165     LinkIterator myFinal; //!< Final element of exclusive sorting range
0166     int          myDigit; //!< Bit number used for partition operation
0167   };
0168 
0169   //! Functor class to run sorting in parallel.
0170   class Functor
0171   {
0172   public:
0173     Functor(const SortRange (&aSplits)[2], const bool isParallel)
0174         : mySplits(aSplits),
0175           myIsParallel(isParallel)
0176     {
0177     }
0178 
0179     //! Runs sorting function for the given range.
0180     void operator()(const int theIndex) const
0181     {
0182       RadixSorter::Sort(mySplits[theIndex].myStart,
0183                         mySplits[theIndex].myFinal,
0184                         mySplits[theIndex].myDigit,
0185                         myIsParallel);
0186     }
0187 
0188   private:
0189     void operator=(const Functor&) = delete;
0190 
0191   private:
0192     const SortRange (&mySplits)[2];
0193     bool myIsParallel;
0194   };
0195 
0196 public:
0197   static void Sort(LinkIterator theStart,
0198                    LinkIterator theFinal,
0199                    int          theDigit,
0200                    const bool   isParallel)
0201   {
0202     if (theDigit < 24)
0203     {
0204       BVH::RadixSorter::perform(theStart, theFinal, theDigit);
0205     }
0206     else
0207     {
0208       LinkIterator anOffset   = std::partition(theStart, theFinal, BitPredicate(theDigit));
0209       SortRange    aSplits[2] = {{theStart, anOffset, theDigit - 1},
0210                                  {anOffset, theFinal, theDigit - 1}};
0211 
0212       OSD_Parallel::For(0, 2, Functor(aSplits, isParallel), !isParallel);
0213     }
0214   }
0215 
0216 protected:
0217   // Performs MSD (most significant digit) radix sort.
0218   static void perform(LinkIterator theStart, LinkIterator theFinal, int theDigit = 29)
0219   {
0220     while (theStart != theFinal && theDigit >= 0)
0221     {
0222       LinkIterator anOffset = std::partition(theStart, theFinal, BitPredicate(theDigit--));
0223       perform(theStart, anOffset, theDigit);
0224       theStart = anOffset;
0225     }
0226   }
0227 };
0228 } // namespace BVH
0229 
0230 //=================================================================================================
0231 
0232 template <class T, int N>
0233 void BVH_RadixSorter<T, N>::Perform(BVH_Set<T, N>* theSet, const int theStart, const int theFinal)
0234 {
0235   Standard_STATIC_ASSERT(N == 2 || N == 3 || N == 4);
0236 
0237   const int aDimension = 1024;
0238   const int aNbEffComp = N == 2 ? 2 : 3; // 4th component is ignored
0239 
0240   const BVH_VecNt aSceneMin = myBox.CornerMin();
0241   const BVH_VecNt aSceneMax = myBox.CornerMax();
0242 
0243   BVH_VecNt aNodeMinSizeVecT(static_cast<T>(BVH::THE_NODE_MIN_SIZE));
0244   BVH::BoxMinMax<T, N>::CwiseMax(aNodeMinSizeVecT, aSceneMax - aSceneMin);
0245 
0246   const BVH_VecNt aReverseSize = BVH_VecNt(static_cast<T>(aDimension)) / aNodeMinSizeVecT;
0247 
0248   myEncodedLinks = new NCollection_Shared<NCollection_Array1<BVH_EncodedLink>>(theStart, theFinal);
0249 
0250   // Step 1 -- Assign Morton code to each primitive using LUT for faster encoding
0251   for (int aPrimIdx = theStart; aPrimIdx <= theFinal; ++aPrimIdx)
0252   {
0253     const BVH_VecNt aCenter = theSet->Box(aPrimIdx).Center();
0254     const BVH_VecNt aVoxelF = (aCenter - aSceneMin) * aReverseSize;
0255 
0256     // Compute voxel coordinates clamped to valid range
0257     const int aVoxelX =
0258       std::clamp(BVH::IntFloor(BVH::VecComp<T, N>::Get(aVoxelF, 0)), 0, aDimension - 1);
0259     const int aVoxelY =
0260       std::clamp(BVH::IntFloor(BVH::VecComp<T, N>::Get(aVoxelF, 1)), 0, aDimension - 1);
0261     const int aVoxelZ =
0262       (aNbEffComp > 2)
0263         ? std::clamp(BVH::IntFloor(BVH::VecComp<T, N>::Get(aVoxelF, 2)), 0, aDimension - 1)
0264         : 0;
0265 
0266     // Use LUT-based Morton code encoding for better performance
0267     const unsigned int aMortonCode = BVH::EncodeMortonCode(static_cast<unsigned int>(aVoxelX),
0268                                                            static_cast<unsigned int>(aVoxelY),
0269                                                            static_cast<unsigned int>(aVoxelZ));
0270 
0271     myEncodedLinks->ChangeValue(aPrimIdx) = BVH_EncodedLink(aMortonCode, aPrimIdx);
0272   }
0273 
0274   // Step 2 -- Sort primitives by their Morton codes using radix sort
0275   BVH::RadixSorter::Sort(myEncodedLinks->begin(), myEncodedLinks->end(), 29, this->IsParallel());
0276 
0277   NCollection_Array1<int> aLinkMap(theStart, theFinal);
0278   for (int aLinkIdx = theStart; aLinkIdx <= theFinal; ++aLinkIdx)
0279   {
0280     aLinkMap(myEncodedLinks->Value(aLinkIdx).second) = aLinkIdx;
0281   }
0282 
0283   // Step 3 -- Rearranging primitive list according to Morton codes (in place)
0284   int aPrimIdx = theStart;
0285   while (aPrimIdx <= theFinal)
0286   {
0287     const int aSortIdx = aLinkMap(aPrimIdx);
0288     if (aPrimIdx != aSortIdx)
0289     {
0290       theSet->Swap(aPrimIdx, aSortIdx);
0291       std::swap(aLinkMap(aPrimIdx), aLinkMap(aSortIdx));
0292     }
0293     else
0294     {
0295       ++aPrimIdx;
0296     }
0297   }
0298 }
0299 
0300 #endif // _BVH_RadixSorter_Header