diff --git a/src/engine/client/backend/vulkan/backend_vulkan.cpp b/src/engine/client/backend/vulkan/backend_vulkan.cpp index 4cf2b1c2e..27ec8f093 100644 --- a/src/engine/client/backend/vulkan/backend_vulkan.cpp +++ b/src/engine/client/backend/vulkan/backend_vulkan.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -160,6 +161,20 @@ class CCommandProcessorFragment_Vulkan : public CCommandProcessorFragment_GLBase size_t m_OffsetToAlign; SMemoryHeapElement *m_pElementInHeap; [[nodiscard]] bool operator>(const SMemoryHeapQueueElement &Other) const { return m_AllocationSize > Other.m_AllocationSize; } + struct SMemoryHeapQueueElementFind + { + // respects alignment requirements + constexpr bool operator()(const SMemoryHeapQueueElement &Val, const std::pair &Other) const + { + auto AllocSize = Other.first; + auto AllocAlignment = Other.second; + size_t ExtraSizeAlign = Val.m_OffsetInHeap % AllocAlignment; + if(ExtraSizeAlign != 0) + ExtraSizeAlign = AllocAlignment - ExtraSizeAlign; + size_t RealAllocSize = AllocSize + ExtraSizeAlign; + return Val.m_AllocationSize < RealAllocSize; + } + }; }; typedef std::multiset> TMemoryHeapQueue; @@ -202,24 +217,32 @@ class CCommandProcessorFragment_Vulkan : public CCommandProcessorFragment_GLBase } else { - // calculate the alignment - size_t ExtraSizeAlign = m_Elements.begin()->m_OffsetInHeap % AllocAlignment; - if(ExtraSizeAlign != 0) - ExtraSizeAlign = AllocAlignment - ExtraSizeAlign; - size_t RealAllocSize = AllocSize + ExtraSizeAlign; - // check if there is enough space in this instance - if(m_Elements.begin()->m_AllocationSize < RealAllocSize) + if(bool(SMemoryHeapQueueElement::SMemoryHeapQueueElementFind{}(*m_Elements.begin(), std::make_pair(AllocSize, AllocAlignment)))) { return false; } else { - auto TopEl = *m_Elements.begin(); + // see SMemoryHeapQueueElement::operator> + SMemoryHeapQueueElement FindAllocSize; + FindAllocSize.m_AllocationSize = AllocSize; + // find upper bound for a allocation size + auto Upper = m_Elements.upper_bound(FindAllocSize); + // then find the first entry that respects alignment, this is a linear search! + auto FoundEl = std::lower_bound(std::make_reverse_iterator(Upper), m_Elements.rend(), std::make_pair(AllocSize, AllocAlignment), SMemoryHeapQueueElement::SMemoryHeapQueueElementFind{}); + + auto TopEl = *FoundEl; m_Elements.erase(TopEl.m_pElementInHeap->m_InQueue); TopEl.m_pElementInHeap->m_InUse = true; + // calculate the real alloc size + alignment offset + size_t ExtraSizeAlign = TopEl.m_OffsetInHeap % AllocAlignment; + if(ExtraSizeAlign != 0) + ExtraSizeAlign = AllocAlignment - ExtraSizeAlign; + size_t RealAllocSize = AllocSize + ExtraSizeAlign; + // the heap element gets children TopEl.m_pElementInHeap->m_pLeft = std::make_unique(); TopEl.m_pElementInHeap->m_pLeft->m_AllocationSize = RealAllocSize;