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:
parent
609614f735
commit
977fdd360c
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue