Optimized VmaBlockMetadata_Linear::FreeAtOffset to use binary search.

Refactored VmaBinaryFindFirstNotLess.
diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h
index 9b3a512..1d42070 100644
--- a/src/vk_mem_alloc.h
+++ b/src/vk_mem_alloc.h
@@ -3003,8 +3003,8 @@
 Returned value is the found element, if present in the collection or place where

 new element with value (key) should be inserted.

 */

-template <typename IterT, typename KeyT, typename CmpT>

-static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpT cmp)

+template <typename CmpLess, typename IterT, typename KeyT>

+static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpLess cmp)

 {

     size_t down = 0, up = (end - beg);

     while(down < up)

@@ -3387,23 +3387,18 @@
     return false;

 }

 

-template<typename CmpLess, typename VectorT>

-size_t VmaVectorFindSorted(const VectorT& vector, const typename VectorT::value_type& value)

+template<typename CmpLess, typename IterT, typename KeyT>

+IterT VmaVectorFindSorted(const IterT& beg, const IterT& end, const KeyT& value)

 {

     CmpLess comparator;

-    typename VectorT::iterator it = VmaBinaryFindFirstNotLess(

-        vector.data(),

-        vector.data() + vector.size(),

-        value,

-        comparator);

-    if(it != vector.size() && !comparator(*it, value) && !comparator(value, *it))

+    typename IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(

+        beg, end, value, comparator);

+    if(it == end ||

+        !comparator(*it, value) && !comparator(value, *it))

     {

-        return it - vector.begin();

+        return it;

     }

-    else

-    {

-        return vector.size();

-    }

+    return end;

 }

 

 ////////////////////////////////////////////////////////////////////////////////

@@ -4338,6 +4333,22 @@
     VmaSuballocationType type;

 };

 

+// Comparator for offsets.

+struct VmaSuballocationOffsetLess

+{

+    bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const

+    {

+        return lhs.offset < rhs.offset;

+    }

+};

+struct VmaSuballocationOffsetGreater

+{

+    bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const

+    {

+        return lhs.offset > rhs.offset;

+    }

+};

+

 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;

 

 // Cost of one additional allocation lost, as equivalent in bytes.

@@ -8754,7 +8765,7 @@
         }

     }

 

-    // Last allocation in 2-part ring buffer or top of 2nd stack (same logic).

+    // Last allocation in 2-part ring buffer or top of upper stack (same logic).

     if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||

         m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)

     {

@@ -8781,16 +8792,20 @@
     }

 

     // Item from the middle of 1st vector.

-    // TODO optimize using binary search.

-    for(size_t i = m_1stNullItemsBeginCount + 1; i < suballocations1st.size(); ++i)

     {

-        VmaSuballocation& currSuballoc = suballocations1st[i];

-        if(currSuballoc.offset == offset)

+        VmaSuballocation refSuballoc;

+        refSuballoc.offset = offset;

+        // Rest of members stays uninitialized intentionally for better performance.

+        SuballocationVectorType::iterator it = VmaVectorFindSorted<VmaSuballocationOffsetLess>(

+            suballocations1st.begin() + m_1stNullItemsBeginCount,

+            suballocations1st.end(),

+            refSuballoc);

+        if(it != suballocations1st.end())

         {

-            currSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;

-            currSuballoc.hAllocation = VK_NULL_HANDLE;

+            it->type = VMA_SUBALLOCATION_TYPE_FREE;

+            it->hAllocation = VK_NULL_HANDLE;

             ++m_1stNullItemsMiddleCount;

-            m_SumFreeSize += currSuballoc.size;

+            m_SumFreeSize += it->size;

             CleanupAfterFree();

             return;

         }

@@ -8799,19 +8814,20 @@
     if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)

     {

         // Item from the middle of 2nd vector.

-        // TODO optimize using binary search. Careful when DOUBLE_STACK - suballocations are then sorted in reverse order of offsets.

-        for(size_t i = 0; i < suballocations2nd.size() - 1; ++i)

+        VmaSuballocation refSuballoc;

+        refSuballoc.offset = offset;

+        // Rest of members stays uninitialized intentionally for better performance.

+        SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?

+            VmaVectorFindSorted<VmaSuballocationOffsetLess>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc) :

+            VmaVectorFindSorted<VmaSuballocationOffsetGreater>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc);

+        if(it != suballocations2nd.end())

         {

-            VmaSuballocation& currSuballoc = suballocations2nd[i];

-            if(currSuballoc.offset == offset)

-            {

-                currSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;

-                currSuballoc.hAllocation = VK_NULL_HANDLE;

-                ++m_2ndNullItemsCount;

-                m_SumFreeSize += currSuballoc.size;

-                CleanupAfterFree();

-                return;

-            }

+            it->type = VMA_SUBALLOCATION_TYPE_FREE;

+            it->hAllocation = VK_NULL_HANDLE;

+            ++m_2ndNullItemsCount;

+            m_SumFreeSize += it->size;

+            CleanupAfterFree();

+            return;

         }

     }