mirror of
https://github.com/ZDoom/Raze.git
synced 2024-12-13 14:10:54 +00:00
94aa556953
For main resource data this is probably unnecessary - most resources are never cached with the exception of sounds and textures, which are loaded permanently anyway. But for hardware textures this is different. Due to the poor precaching it is impossible to selectively evict hardware textures that are not needed any longer, so for this an MRU cache is really needed so that they do not accumulate and congest the video RAM in the process.
129 lines
2.2 KiB
C++
129 lines
2.2 KiB
C++
|
|
|
|
struct CacheNode
|
|
{
|
|
size_t size;
|
|
CacheNode *next, *prev;
|
|
int lockCount;
|
|
int lastusetick; // This is to ensure that a node lives for the duration of the frame it is last axxessed on
|
|
|
|
virtual ~CacheNode();
|
|
virtual void Purge() = 0; // needs to be implemented by the child class to allow different types of menory to be used.
|
|
};
|
|
|
|
class Cache
|
|
{
|
|
size_t maxSize;
|
|
size_t currentSize;
|
|
CacheNode purgeHead;
|
|
int currenttick;
|
|
|
|
public:
|
|
Cache()
|
|
{
|
|
maxSize = 100'000'000;
|
|
currentSize = 0;
|
|
purgeHead = { 0, &purgeHead, &purgeHead, 0 };
|
|
}
|
|
|
|
SetSize(size_t newsize)
|
|
{
|
|
if (newsize < maxSize) Purge();
|
|
maxSize = newsize;
|
|
}
|
|
|
|
void AddToPurgeList(CacheNode *h)
|
|
{
|
|
h->prev = purgeHead.prev;
|
|
purgeHead.prev->next = h;
|
|
h->next = &purgeHead;
|
|
purgeHead.prev = h;
|
|
}
|
|
|
|
void RemoveFromPurgeList(CacheNode *h)
|
|
{
|
|
h->prev->next = h->next;
|
|
h->next->prev = h->prev;
|
|
}
|
|
|
|
void Alloc(CacheNode *h)
|
|
{
|
|
currentSize += h->size;
|
|
if (currentSize > maxSize)
|
|
{
|
|
Purge();
|
|
}
|
|
AddToPurgeList(h);
|
|
}
|
|
|
|
void Release(CacheNode *h)
|
|
{
|
|
currentSize -= h->size;
|
|
RemoveFromPurgeList(h);
|
|
}
|
|
|
|
void Validata(CacheNode *h)
|
|
{
|
|
if (h->LockCount == 0)
|
|
{
|
|
// Move node to the top of the linked list.
|
|
RemoveFromPurgeList(h);
|
|
AddToPurgeList(h);
|
|
}
|
|
}
|
|
|
|
void *Lock(CacheNode *h)
|
|
{
|
|
// This merely locks the node. Allocation must be reported separately.
|
|
assert(h != NULL);
|
|
if (h->lockCount == 0)
|
|
{
|
|
RemoveFromPurgeList(h);
|
|
}
|
|
|
|
h->lockCount++;
|
|
return h->ptr;
|
|
}
|
|
|
|
void Unlock(CacheNode *h)
|
|
{
|
|
assert(h != NULL);
|
|
if (h->lockCount > 0)
|
|
{
|
|
h->lockCount--;
|
|
if (h->lockCount == 0)
|
|
{
|
|
AddToPurgeList();
|
|
if (currentSize > maxSize)
|
|
{
|
|
Release(h);
|
|
h->Purge();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PurgeCache(bool all = false)
|
|
{
|
|
// Do not delete from the list while it's being iterated. Better store in a temporary list and delete from there.
|
|
TArray<CacheNode *> nodesToPurge(10);
|
|
int purgeSize = 0;
|
|
for (CacheNode *node = purgeHead.next; node != &purgeHead; node = node->next)
|
|
{
|
|
if (node->lastusetick < currenttick)
|
|
{
|
|
nodesToPurge.Push(npde);
|
|
purgeSize += node->size;
|
|
if (currentSize - purgeSize < maxSize && !all) break;
|
|
}
|
|
}
|
|
for (auto h : nodesToPurge)
|
|
{
|
|
Release(h);
|
|
h->Purge();
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|