mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-06-04 05:35:00 -06:00
LatteBufferCache: fix interval lookups after range splits
IntervalTree2 used std::map::find() for overlap-based queries, which can miss a valid covering node after range splits or resizes. Switch these lookups to a lower_bound-based first-overlap search so reserve and retrieve paths always find the correct cache range. Also keep the original heap allocation offset separate from the visible cache offset and update page metadata correctly when shrinking a node. That keeps frees and deferred releases aligned with the underlying allocation and avoids stale page state after front trims.
This commit is contained in:
parent
391478b307
commit
8e168567c2
@ -46,10 +46,18 @@ class IntervalTree2
|
|||||||
std::map<InternalRange, TNodeObject*> m_map;
|
std::map<InternalRange, TNodeObject*> m_map;
|
||||||
std::vector<TNodeObject*> m_tempObjectArray;
|
std::vector<TNodeObject*> m_tempObjectArray;
|
||||||
|
|
||||||
|
typename std::map<InternalRange, TNodeObject*>::iterator findFirstOverlapping(TRangeData rangeBegin, TRangeData rangeEnd)
|
||||||
|
{
|
||||||
|
auto itr = m_map.lower_bound(InternalRange(rangeBegin, rangeBegin + 1));
|
||||||
|
if (itr == m_map.end() || (*itr).first.rangeBegin >= rangeEnd)
|
||||||
|
return m_map.end();
|
||||||
|
return itr;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TNodeObject* getRange(TRangeData rangeBegin, TRangeData rangeEnd)
|
TNodeObject* getRange(TRangeData rangeBegin, TRangeData rangeEnd)
|
||||||
{
|
{
|
||||||
auto itr = m_map.find(InternalRange(rangeBegin, rangeEnd));
|
auto itr = findFirstOverlapping(rangeBegin, rangeEnd);
|
||||||
if (itr == m_map.cend())
|
if (itr == m_map.cend())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (rangeBegin < (*itr).first.rangeBegin)
|
if (rangeBegin < (*itr).first.rangeBegin)
|
||||||
@ -61,7 +69,7 @@ public:
|
|||||||
|
|
||||||
TNodeObject* getRangeByPoint(TRangeData rangeOffset)
|
TNodeObject* getRangeByPoint(TRangeData rangeOffset)
|
||||||
{
|
{
|
||||||
auto itr = m_map.find(InternalRange(rangeOffset, rangeOffset+1)); // todo - better to use custom comparator instead of +1?
|
auto itr = findFirstOverlapping(rangeOffset, rangeOffset + 1);
|
||||||
if (itr == m_map.cend())
|
if (itr == m_map.cend())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
cemu_assert_debug(rangeOffset >= (*itr).first.rangeBegin);
|
cemu_assert_debug(rangeOffset >= (*itr).first.rangeBegin);
|
||||||
@ -74,7 +82,7 @@ public:
|
|||||||
if (rangeEnd == rangeBegin)
|
if (rangeEnd == rangeBegin)
|
||||||
return;
|
return;
|
||||||
InternalRange range(rangeBegin, rangeEnd);
|
InternalRange range(rangeBegin, rangeEnd);
|
||||||
auto itr = m_map.find(range);
|
auto itr = findFirstOverlapping(rangeBegin, rangeEnd);
|
||||||
if (itr == m_map.cend())
|
if (itr == m_map.cend())
|
||||||
{
|
{
|
||||||
// new entry
|
// new entry
|
||||||
@ -124,10 +132,9 @@ public:
|
|||||||
void removeRange(TRangeData rangeBegin, TRangeData rangeEnd)
|
void removeRange(TRangeData rangeBegin, TRangeData rangeEnd)
|
||||||
{
|
{
|
||||||
InternalRange range(rangeBegin, rangeEnd);
|
InternalRange range(rangeBegin, rangeEnd);
|
||||||
auto itr = m_map.find(range);
|
auto itr = findFirstOverlapping(rangeBegin, rangeEnd);
|
||||||
if (itr == m_map.cend())
|
if (itr == m_map.cend())
|
||||||
return;
|
return;
|
||||||
cemu_assert_debug(itr == m_map.lower_bound(range));
|
|
||||||
while (itr != m_map.cend() && (*itr).first.rangeBegin < rangeEnd)
|
while (itr != m_map.cend() && (*itr).first.rangeBegin < rangeEnd)
|
||||||
{
|
{
|
||||||
if ((*itr).first.rangeBegin >= rangeBegin && (*itr).first.rangeEnd <= rangeEnd)
|
if ((*itr).first.rangeBegin >= rangeBegin && (*itr).first.rangeEnd <= rangeEnd)
|
||||||
@ -225,11 +232,9 @@ public:
|
|||||||
template<typename TFunc>
|
template<typename TFunc>
|
||||||
void forEachOverlapping(TRangeData rangeBegin, TRangeData rangeEnd, TFunc f)
|
void forEachOverlapping(TRangeData rangeBegin, TRangeData rangeEnd, TFunc f)
|
||||||
{
|
{
|
||||||
InternalRange range(rangeBegin, rangeEnd);
|
auto itr = findFirstOverlapping(rangeBegin, rangeEnd);
|
||||||
auto itr = m_map.find(range);
|
|
||||||
if (itr == m_map.cend())
|
if (itr == m_map.cend())
|
||||||
return;
|
return;
|
||||||
cemu_assert_debug(itr == m_map.lower_bound(range));
|
|
||||||
while (itr != m_map.cend() && (*itr).first.rangeBegin < rangeEnd)
|
while (itr != m_map.cend() && (*itr).first.rangeBegin < rangeEnd)
|
||||||
{
|
{
|
||||||
f((*itr).second, rangeBegin, rangeEnd);
|
f((*itr).second, rangeBegin, rangeEnd);
|
||||||
@ -282,7 +287,9 @@ public:
|
|||||||
{
|
{
|
||||||
cemu_assert_debug(m_hasCacheAlloc == false);
|
cemu_assert_debug(m_hasCacheAlloc == false);
|
||||||
cemu_assert_debug(m_rangeEnd > m_rangeBegin);
|
cemu_assert_debug(m_rangeEnd > m_rangeBegin);
|
||||||
m_hasCacheAlloc = g_gpuBufferHeap->allocOffset(m_rangeEnd - m_rangeBegin, CACHE_PAGE_SIZE, m_cacheOffset);
|
m_hasCacheAlloc = g_gpuBufferHeap->allocOffset(m_rangeEnd - m_rangeBegin, CACHE_PAGE_SIZE, m_cacheAllocOffset);
|
||||||
|
if (m_hasCacheAlloc)
|
||||||
|
m_cacheOffset = m_cacheAllocOffset;
|
||||||
return m_hasCacheAlloc;
|
return m_hasCacheAlloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,8 +297,10 @@ public:
|
|||||||
{
|
{
|
||||||
if (m_hasCacheAlloc)
|
if (m_hasCacheAlloc)
|
||||||
{
|
{
|
||||||
g_gpuBufferHeap->freeOffset(m_cacheOffset);
|
g_gpuBufferHeap->freeOffset(m_cacheAllocOffset);
|
||||||
m_hasCacheAlloc = false;
|
m_hasCacheAlloc = false;
|
||||||
|
m_cacheAllocOffset = 0;
|
||||||
|
m_cacheOffset = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,6 +313,15 @@ public:
|
|||||||
return m_cacheOffset + relOffset;
|
return m_cacheOffset + relOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 GetCacheAllocOffset() const
|
||||||
|
{
|
||||||
|
return m_cacheAllocOffset;
|
||||||
|
}
|
||||||
|
uint32 GetCacheOffset() const
|
||||||
|
{
|
||||||
|
return m_cacheOffset;
|
||||||
|
}
|
||||||
|
|
||||||
void writeStreamout(MPTR rangeBegin, MPTR rangeEnd)
|
void writeStreamout(MPTR rangeBegin, MPTR rangeEnd)
|
||||||
{
|
{
|
||||||
if ((rangeBegin & 0xF))
|
if ((rangeBegin & 0xF))
|
||||||
@ -521,6 +539,7 @@ private:
|
|||||||
MPTR m_rangeBegin;
|
MPTR m_rangeBegin;
|
||||||
MPTR m_rangeEnd; // (exclusive)
|
MPTR m_rangeEnd; // (exclusive)
|
||||||
bool m_hasCacheAlloc{ false };
|
bool m_hasCacheAlloc{ false };
|
||||||
|
uint32 m_cacheAllocOffset{0};
|
||||||
uint32 m_cacheOffset{ 0 };
|
uint32 m_cacheOffset{ 0 };
|
||||||
// usage
|
// usage
|
||||||
uint32 m_lastDrawcall;
|
uint32 m_lastDrawcall;
|
||||||
@ -549,7 +568,7 @@ private:
|
|||||||
~BufferCacheNode()
|
~BufferCacheNode()
|
||||||
{
|
{
|
||||||
if (m_hasCacheAlloc)
|
if (m_hasCacheAlloc)
|
||||||
g_deallocateQueue.emplace_back(m_cacheOffset); // release after current drawcall
|
g_deallocateQueue.emplace_back(m_cacheAllocOffset); // release after current drawcall
|
||||||
// remove from array
|
// remove from array
|
||||||
auto temp = s_allCacheNodes.back();
|
auto temp = s_allCacheNodes.back();
|
||||||
s_allCacheNodes.pop_back();
|
s_allCacheNodes.pop_back();
|
||||||
@ -686,12 +705,44 @@ private:
|
|||||||
|
|
||||||
void shrink(MPTR newRangeBegin, MPTR newRangeEnd)
|
void shrink(MPTR newRangeBegin, MPTR newRangeEnd)
|
||||||
{
|
{
|
||||||
cemu_assert_debug(newRangeBegin >= m_rangeBegin);
|
const MPTR oldRangeBegin = m_rangeBegin;
|
||||||
cemu_assert_debug(newRangeEnd >= m_rangeEnd);
|
const MPTR oldRangeEnd = m_rangeEnd;
|
||||||
cemu_assert_debug(newRangeEnd > m_rangeBegin);
|
const uint32 oldPageCount = (uint32)m_pageInfo.size();
|
||||||
assert_dbg(); // todo (resize page array)
|
|
||||||
|
cemu_assert_debug((newRangeBegin % CACHE_PAGE_SIZE) == 0);
|
||||||
|
cemu_assert_debug((newRangeEnd % CACHE_PAGE_SIZE) == 0);
|
||||||
|
cemu_assert_debug(newRangeBegin >= oldRangeBegin);
|
||||||
|
cemu_assert_debug(newRangeEnd <= oldRangeEnd);
|
||||||
|
cemu_assert_debug(newRangeEnd > newRangeBegin);
|
||||||
|
|
||||||
|
const uint32 trimFrontBytes = newRangeBegin - oldRangeBegin;
|
||||||
|
const uint32 trimFrontPages = trimFrontBytes / CACHE_PAGE_SIZE;
|
||||||
|
const uint32 newPageCount = (newRangeEnd - newRangeBegin) / CACHE_PAGE_SIZE;
|
||||||
|
cemu_assert_debug(trimFrontPages <= oldPageCount);
|
||||||
|
cemu_assert_debug(newPageCount <= oldPageCount - trimFrontPages);
|
||||||
|
|
||||||
|
if (trimFrontPages != 0)
|
||||||
|
{
|
||||||
|
m_pageInfo.erase(m_pageInfo.begin(), m_pageInfo.begin() + trimFrontPages);
|
||||||
|
if (m_hasCacheAlloc)
|
||||||
|
m_cacheOffset += trimFrontBytes;
|
||||||
|
}
|
||||||
|
m_pageInfo.resize(newPageCount);
|
||||||
|
|
||||||
m_rangeBegin = newRangeBegin;
|
m_rangeBegin = newRangeBegin;
|
||||||
m_rangeEnd = newRangeEnd;
|
m_rangeEnd = newRangeEnd;
|
||||||
|
|
||||||
|
if (m_hasInvalidation)
|
||||||
|
{
|
||||||
|
m_invalidationRangeBegin = std::max(m_invalidationRangeBegin, newRangeBegin);
|
||||||
|
m_invalidationRangeEnd = std::min(m_invalidationRangeEnd, newRangeEnd);
|
||||||
|
if (m_invalidationRangeBegin >= m_invalidationRangeEnd)
|
||||||
|
m_hasInvalidation = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hasStreamoutData = std::any_of(m_pageInfo.cbegin(), m_pageInfo.cend(), [](const auto& pageInfo) {
|
||||||
|
return pageInfo.hasStreamoutData;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64 hashPage(uint8* mem)
|
static uint64 hashPage(uint8* mem)
|
||||||
@ -864,7 +915,16 @@ public:
|
|||||||
// retry allocation
|
// retry allocation
|
||||||
if (!newRange->allocateCacheMemory())
|
if (!newRange->allocateCacheMemory())
|
||||||
{
|
{
|
||||||
cemuLog_log(LogType::Force, "Out-of-memory in GPU buffer (trying to allocate: {}KB) Cleaning up cache...", (rangeEnd - rangeBegin + 1023) / 1024);
|
uint32 heapSize;
|
||||||
|
uint32 allocationSize;
|
||||||
|
uint32 allocNum;
|
||||||
|
g_gpuBufferHeap->getStats(heapSize, allocationSize, allocNum);
|
||||||
|
cemuLog_log(LogType::Force,
|
||||||
|
"Out-of-memory in GPU buffer (trying to allocate: {}KB, used: {}/{}MB, allocations: {}) Cleaning up cache...",
|
||||||
|
(rangeEnd - rangeBegin + 1023) / 1024,
|
||||||
|
allocationSize / (1024 * 1024),
|
||||||
|
heapSize / (1024 * 1024),
|
||||||
|
allocNum);
|
||||||
CleanupCacheAggressive(rangeBegin, rangeEnd);
|
CleanupCacheAggressive(rangeBegin, rangeEnd);
|
||||||
if (!newRange->allocateCacheMemory())
|
if (!newRange->allocateCacheMemory())
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user