Merge pull request #2164 from CookiePLMonster/vertex-cache-improvements

Vertex buffer cache improvements
This commit is contained in:
Luke Usher 2021-03-18 13:48:27 +00:00 committed by GitHub
commit d9b994c357
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 47 deletions

View File

@ -27,7 +27,7 @@ void InitHasher()
}
}
uint64_t ComputeHash(void* data, size_t len)
uint64_t ComputeHash(const void* data, size_t len)
{
if (g_HashAlgorithm == HASH_NONE) {
InitHasher();

View File

@ -29,6 +29,6 @@
#include <stdint.h>
uint64_t ComputeHash(void* data, size_t len);
uint64_t ComputeHash(const void* data, size_t len);
#endif

View File

@ -111,11 +111,6 @@ void CxbxPatchedStream::Activate(CxbxDrawContext *pDrawContext, UINT HostStreamN
}
}
CxbxPatchedStream::CxbxPatchedStream()
{
isValid = false;
}
void CxbxPatchedStream::Clear()
{
if (bCachedHostVertexStreamZeroDataIsAllocated) {
@ -136,12 +131,6 @@ CxbxPatchedStream::~CxbxPatchedStream()
Clear();
}
CxbxVertexBufferConverter::CxbxVertexBufferConverter()
{
m_uiNbrStreams = 0;
m_pCxbxVertexDeclaration = nullptr;
}
// TODO: CountActiveD3DStreams must be removed once we can rely on CxbxGetVertexDeclaration always being set
int CountActiveD3DStreams()
{
@ -155,7 +144,7 @@ int CountActiveD3DStreams()
return StreamCount;
}
UINT CxbxVertexBufferConverter::GetNbrStreams(CxbxDrawContext *pDrawContext)
UINT CxbxVertexBufferConverter::GetNbrStreams(CxbxDrawContext *pDrawContext) const
{
// Draw..Up always have one stream
if (pDrawContext->pXboxVertexStreamZeroData != xbox::zeroptr) {
@ -195,9 +184,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 +205,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();
}
}
@ -232,7 +225,8 @@ void CxbxVertexBufferConverter::PrintStats()
void CxbxVertexBufferConverter::ConvertStream
(
CxbxDrawContext *pDrawContext,
CxbxDrawContext *pDrawContext,
CxbxVertexDeclaration* pCxbxVertexDeclaration,
UINT uiStream
)
{
@ -241,13 +235,13 @@ void CxbxVertexBufferConverter::ConvertStream
CxbxVertexShaderStreamInfo *pVertexShaderStreamInfo = nullptr;
UINT XboxStreamNumber = uiStream;
if (m_pCxbxVertexDeclaration != nullptr) {
if (uiStream > m_pCxbxVertexDeclaration->NumberOfVertexStreams) {
if (pCxbxVertexDeclaration != nullptr) {
if (uiStream > pCxbxVertexDeclaration->NumberOfVertexStreams) {
LOG_TEST_CASE("uiStream > NumberOfVertexStreams");
return;
}
pVertexShaderStreamInfo = &(m_pCxbxVertexDeclaration->VertexStreams[uiStream]);
pVertexShaderStreamInfo = &(pCxbxVertexDeclaration->VertexStreams[uiStream]);
XboxStreamNumber = pVertexShaderStreamInfo->XboxStreamIndex;
}
@ -327,20 +321,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
@ -631,7 +621,7 @@ void CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext)
if ((pDrawContext->XboxPrimitiveType < xbox::X_D3DPT_POINTLIST) || (pDrawContext->XboxPrimitiveType > xbox::X_D3DPT_POLYGON))
CxbxKrnlCleanup("Unknown primitive type: 0x%.02X\n", pDrawContext->XboxPrimitiveType);
m_pCxbxVertexDeclaration = CxbxGetVertexDeclaration();
CxbxVertexDeclaration* pCxbxVertexDeclaration = CxbxGetVertexDeclaration();
// When this is an indexed draw, take the index buffer into account
if (pDrawContext->pXboxIndexData) {
@ -652,14 +642,14 @@ void CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext)
}
// Get the number of streams
m_uiNbrStreams = GetNbrStreams(pDrawContext);
if (m_uiNbrStreams > X_VSH_MAX_STREAMS) {
LOG_TEST_CASE("m_uiNbrStreams count > max number of streams");
m_uiNbrStreams = X_VSH_MAX_STREAMS;
}
UINT nbrStreams = GetNbrStreams(pDrawContext);
if (nbrStreams > X_VSH_MAX_STREAMS) {
LOG_TEST_CASE("nbrStreams count > max number of streams");
nbrStreams = X_VSH_MAX_STREAMS;
}
for(UINT i = 0; i < m_uiNbrStreams; i++) {
ConvertStream(pDrawContext, i);
for(UINT i = 0; i < nbrStreams; i++) {
ConvertStream(pDrawContext, pCxbxVertexDeclaration, i);
}
if (pDrawContext->XboxPrimitiveType == xbox::X_D3DPT_QUADSTRIP) {

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
{
@ -54,7 +55,7 @@ CxbxDrawContext;
class CxbxPatchedStream
{
public:
CxbxPatchedStream();
CxbxPatchedStream() = default;
~CxbxPatchedStream();
void Clear();
void Activate(CxbxDrawContext *pDrawContext, UINT HostStreamNumber) const;
@ -75,29 +76,42 @@ public:
class CxbxVertexBufferConverter
{
public:
CxbxVertexBufferConverter();
CxbxVertexBufferConverter() = default;
void Apply(CxbxDrawContext *pPatchDesc);
void PrintStats();
private:
UINT m_uiNbrStreams;
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)));
}
};
// Stack tracking
ULONG m_TotalCacheHits = 0;
ULONG m_TotalCacheMisses = 0;
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
const UINT m_MaxCacheSize = 10000; // Maximum number of entries in the cache
const UINT m_CacheElasticity = 200; // Cache is allowed to grow this much more than maximum before being purged to maximum
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
CxbxVertexDeclaration *m_pCxbxVertexDeclaration;
CxbxPatchedStream& GetPatchedStream(uint64_t dataKey, uint64_t streamInfoKey); // Fetches (or inserts) a patched stream associated with the given key
// Returns the number of streams of a patch
UINT GetNbrStreams(CxbxDrawContext *pPatchDesc);
UINT GetNbrStreams(CxbxDrawContext *pPatchDesc) const;
// Patches the types of the stream
void ConvertStream(CxbxDrawContext *pPatchDesc, UINT uiStream);
void ConvertStream(CxbxDrawContext *pPatchDesc, CxbxVertexDeclaration* pCxbxVertexDeclaration, UINT uiStream);
};
// Inline vertex buffer emulation