Use a compound key in the vertex cache to reduce false cache evictions

Now same data viewed with different vertex elements can coexist
in the cache instead of evicting elements continuously
This commit is contained in:
Silent 2021-03-17 18:24:57 +01:00
parent 609614f735
commit 977fdd360c
No known key found for this signature in database
GPG Key ID: AE53149BB0C45AF1
2 changed files with 30 additions and 12 deletions

View File

@ -195,9 +195,11 @@ inline FLOAT ByteToFloat(const BYTE value)
return ((FLOAT)value) / 255.0f;
}
CxbxPatchedStream& CxbxVertexBufferConverter::GetPatchedStream(uint64_t key)
CxbxPatchedStream& CxbxVertexBufferConverter::GetPatchedStream(uint64_t dataKey, uint64_t streamInfoKey)
{
// First, attempt to fetch an existing patched stream
const StreamKey key{ dataKey, streamInfoKey };
auto it = m_PatchedStreams.find(key);
if (it != m_PatchedStreams.end()) {
m_PatchedStreamUsageList.splice(m_PatchedStreamUsageList.begin(), m_PatchedStreamUsageList, it->second);
@ -214,7 +216,9 @@ CxbxPatchedStream& CxbxVertexBufferConverter::GetPatchedStream(uint64_t key)
// If the cache has exceeded it's upper bound, discard the oldest entries in the cache
if (m_PatchedStreams.size() > (m_MaxCacheSize + m_CacheElasticity)) {
while (m_PatchedStreams.size() > m_MaxCacheSize) {
m_PatchedStreams.erase(m_PatchedStreamUsageList.back().uiVertexDataHash);
const CxbxPatchedStream& streamToDelete = m_PatchedStreamUsageList.back();
m_PatchedStreams.erase({ streamToDelete.uiVertexDataHash, streamToDelete.uiVertexStreamInformationHash });
m_PatchedStreamUsageList.pop_back();
}
}
@ -327,20 +331,16 @@ void CxbxVertexBufferConverter::ConvertStream
const UINT uiVertexCount = pDrawContext->NumVerticesToUse;
const DWORD dwHostVertexDataSize = uiVertexCount * uiHostVertexStride;
const DWORD xboxVertexDataSize = uiVertexCount * uiXboxVertexStride;
uint64_t vertexDataHash = ComputeHash(pXboxVertexData, xboxVertexDataSize);
uint64_t pVertexShaderSteamInfoHash = 0;
if (pVertexShaderStreamInfo != nullptr) {
pVertexShaderSteamInfoHash = ComputeHash(pVertexShaderStreamInfo, sizeof(CxbxVertexShaderStreamInfo));
}
const uint64_t vertexDataHash = ComputeHash(pXboxVertexData, xboxVertexDataSize);
const uint64_t pVertexShaderSteamInfoHash = pVertexShaderStreamInfo != nullptr ? ComputeHash(pVertexShaderStreamInfo->VertexElements,
sizeof(pVertexShaderStreamInfo->VertexElements[0]) * pVertexShaderStreamInfo->NumberOfVertexElements) : 0;
// Lookup implicity inserts a new entry if not exists, so this always works
CxbxPatchedStream& patchedStream = GetPatchedStream(vertexDataHash);
CxbxPatchedStream& patchedStream = GetPatchedStream(vertexDataHash, pVertexShaderSteamInfoHash);
// We check a few fields of the patched stream to protect against hash collisions (rare)
// but also to protect against games using the exact same vertex data for different vertex formats (Test Case: Burnout)
if (patchedStream.isValid && // Check that we found a cached stream
patchedStream.uiVertexStreamInformationHash == pVertexShaderSteamInfoHash && // Check that the vertex conversion is valid
patchedStream.uiCachedHostVertexStride == patchedStream.uiCachedHostVertexStride && // Make sure the host stride didn't change
patchedStream.uiCachedXboxVertexStride == uiXboxVertexStride && // Make sure the Xbox Stride didn't change
patchedStream.uiCachedXboxVertexDataSize == xboxVertexDataSize ) { // Make sure the Xbox Data Size also didn't change

View File

@ -31,6 +31,7 @@
#include "Cxbx.h"
#include "core\hle\D3D8\XbVertexShader.h"
#include "common\util\hasher.h" // For ComputeHash
typedef struct _CxbxDrawContext
{
@ -79,6 +80,23 @@ class CxbxVertexBufferConverter
void Apply(CxbxDrawContext *pPatchDesc);
void PrintStats();
private:
struct StreamKey
{
uint64_t dataKey;
uint64_t streamInfoKey;
bool operator==(const StreamKey& rhs) const {
return this->dataKey == rhs.dataKey && this->streamInfoKey == rhs.streamInfoKey;
}
};
struct StreamKeyHash
{
std::size_t operator()(const StreamKey& k) const {
return static_cast<std::size_t>(ComputeHash(&k, sizeof(k)));
}
};
UINT m_uiNbrStreams;
// Stack tracking
@ -87,9 +105,9 @@ class CxbxVertexBufferConverter
UINT m_MaxCacheSize = 10000; // Maximum number of entries in the cache
UINT m_CacheElasticity = 200; // Cache is allowed to grow this much more than maximum before being purged to maximum
std::unordered_map<uint64_t, std::list<CxbxPatchedStream>::iterator> m_PatchedStreams; // Stores references to patched streams for fast lookup
std::unordered_map<StreamKey, std::list<CxbxPatchedStream>::iterator, StreamKeyHash> m_PatchedStreams; // Stores references to patched streams for fast lookup
std::list<CxbxPatchedStream> m_PatchedStreamUsageList; // Linked list of vertex streams, least recently used is last in the list
CxbxPatchedStream& GetPatchedStream(uint64_t); // Fetches (or inserts) a patched stream associated with the given key
CxbxPatchedStream& GetPatchedStream(uint64_t dataKey, uint64_t streamInfoKey); // Fetches (or inserts) a patched stream associated with the given key
CxbxVertexDeclaration *m_pCxbxVertexDeclaration;