Optimize vertex stream cache for partial buffer access

Drastically reduces the amount of copied and converted data in cases
where any of the following apply:
* BaseVertexIndex is not 0
* LowIndex is not 0
* dwStartVertex is not 0

Also potentially fixes an issue where data past an indexed
vertex buffer was hashed and converted, leading to
potential false positives on dirty checks, maybe more.
This commit is contained in:
Silent 2020-11-26 19:55:01 +01:00
parent b2ca28198e
commit 2480582b0c
No known key found for this signature in database
GPG Key ID: AE53149BB0C45AF1
3 changed files with 24 additions and 25 deletions

View File

@ -6864,7 +6864,7 @@ void CxbxDrawIndexed(CxbxDrawContext &DrawContext)
// for an explanation on the function of the BaseVertexIndex, MinVertexIndex, NumVertices and StartIndex arguments.
HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitive(
/* PrimitiveType = */EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType),
BaseVertexIndex,
/* BaseVertexIndex, = */-CacheEntry.LowIndex, // Base vertex index has been accounted for in the stream conversion, now we need to "un-offset" the index buffer
/* MinVertexIndex = */CacheEntry.LowIndex,
/* NumVertices = */(CacheEntry.HighIndex - CacheEntry.LowIndex) + 1,
/* startIndex = DrawContext.dwStartVertex = */0,
@ -7516,9 +7516,6 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawVertices)
// Draw quadlists using a single 'quad-to-triangle mapping' index buffer :
// Assure & activate that special index buffer :
CxbxAssureQuadListD3DIndexBuffer(/*NrOfQuadIndices=*/DrawContext.dwVertexCount);
// This API's StartVertex argument is multiplied by vertex stride and added to the start of the vertex buffer;
// BaseVertexIndex offers the same functionality on host :
UINT BaseVertexIndex = DrawContext.dwStartVertex;
// Convert quad vertex count to triangle vertex count :
UINT NumVertices = QuadToTriangleVertexCount(DrawContext.dwVertexCount);
// Convert quad primitive count to triangle primitive count :
@ -7528,7 +7525,7 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawVertices)
// Emulate drawing quads by drawing each quad with two indexed triangles :
HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitive(
/*PrimitiveType=*/D3DPT_TRIANGLELIST,
BaseVertexIndex,
/*BaseVertexIndex=*/0, // Base vertex index has been accounted for in the stream conversion
/*MinVertexIndex=*/0,
NumVertices,
/*startIndex=*/0,
@ -7542,7 +7539,7 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawVertices)
// if (StartVertex > 0) LOG_TEST_CASE("StartVertex > 0 (non-quad)"); // Verified test case : XDK Sample (PlayField)
HRESULT hRet = g_pD3DDevice->DrawPrimitive(
EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType),
DrawContext.dwStartVertex,
/*StartVertex=*/0, // Start vertex has been accounted for in the stream conversion
DrawContext.dwHostPrimitiveCount
);
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawPrimitive");
@ -7553,8 +7550,8 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawVertices)
LOG_TEST_CASE("X_D3DPT_LINELOOP"); // TODO : Text-cases needed
assert(DrawContext.dwBaseVertexIndex == 0); // if this fails, it needs to be added to LowIndex and HighIndex :
INDEX16 LowIndex = (INDEX16)DrawContext.dwStartVertex;
INDEX16 HighIndex = (INDEX16)(DrawContext.dwStartVertex + DrawContext.dwHostPrimitiveCount);
INDEX16 LowIndex = 0;
INDEX16 HighIndex = (INDEX16)(DrawContext.dwHostPrimitiveCount);
// Draw the closing line using a helper function (which will SetIndices)
CxbxDrawIndexedClosingLine(LowIndex, HighIndex);
// NOTE : We don't restore the previously active index buffer

View File

@ -261,9 +261,7 @@ void CxbxVertexBufferConverter::ConvertStream
UINT HostStreamNumber = XboxStreamNumber; // Use Xbox stream index on host
uint8_t *pXboxVertexData = xbox::zeroptr;
UINT uiXboxVertexStride = 0;
UINT uiVertexCount = 0;
UINT uiHostVertexStride = 0;
DWORD dwHostVertexDataSize = 0;
uint8_t *pHostVertexData = nullptr;
IDirect3DVertexBuffer *pNewHostVertexBuffer = nullptr;
@ -275,9 +273,7 @@ void CxbxVertexBufferConverter::ConvertStream
pXboxVertexData = (uint8_t *)pDrawContext->pXboxVertexStreamZeroData;
uiXboxVertexStride = pDrawContext->uiXboxVertexStreamZeroStride;
uiVertexCount = pDrawContext->VerticesInBuffer;
uiHostVertexStride = (bNeedVertexPatching) ? pVertexShaderStreamInfo->HostVertexStride : uiXboxVertexStride;
dwHostVertexDataSize = uiVertexCount * uiHostVertexStride;
} else {
xbox::X_STREAMINPUT& XboxStreamInput = GetXboxVertexStreamInput(XboxStreamNumber);
xbox::X_D3DVertexBuffer *pXboxVertexBuffer = XboxStreamInput.VertexBuffer;
@ -298,14 +294,11 @@ void CxbxVertexBufferConverter::ConvertStream
pXboxVertexData += XboxStreamInput.Offset;
uiXboxVertexStride = XboxStreamInput.Stride;
// Set a new (exact) vertex count
uiVertexCount = pDrawContext->VerticesInBuffer;
// Dxbx note : Don't overwrite pDrawContext.dwVertexCount with uiVertexCount, because an indexed draw
// can (and will) use less vertices than the supplied nr of indexes. Thix fixes
// the missing parts in the CompressedVertices sample (in Vertex shader mode).
uiHostVertexStride = (bNeedVertexPatching) ? pVertexShaderStreamInfo->HostVertexStride : uiXboxVertexStride;
dwHostVertexDataSize = uiVertexCount * uiHostVertexStride;
// Copy stream for patching and caching.
bNeedStreamCopy = true;
@ -327,7 +320,17 @@ void CxbxVertexBufferConverter::ConvertStream
}
// Now we have enough information to hash the existing resource and find it in our cache!
DWORD xboxVertexDataSize = uiVertexCount * uiXboxVertexStride;
// To avoid hashing and converting unused vertices, identify the "interesting" region
// basing on the index/starting vertex data
if (pDrawContext->pXboxIndexData != nullptr) {
pXboxVertexData += (pDrawContext->dwBaseVertexIndex + pDrawContext->LowIndex) * uiXboxVertexStride;
} else {
pXboxVertexData += pDrawContext->dwStartVertex * uiXboxVertexStride;
}
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;
@ -634,9 +637,6 @@ void CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext)
m_pCxbxVertexDeclaration = CxbxGetVertexDeclaration();
// If we are drawing from an offset, we know that the vertex count must have
// 'offset' vertices before the first drawn vertices
pDrawContext->VerticesInBuffer = pDrawContext->dwStartVertex + pDrawContext->dwVertexCount;
// When this is an indexed draw, take the index buffer into account
if (pDrawContext->pXboxIndexData) {
// Is the highest index in this buffer not set yet?
@ -646,11 +646,13 @@ void CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext)
LOG_TEST_CASE("HighIndex == 0"); // TODO : If this is never hit, replace entire block by assert(pDrawContext->HighIndex > 0);
WalkIndexBuffer(pDrawContext->LowIndex, pDrawContext->HighIndex, pDrawContext->pXboxIndexData, pDrawContext->dwVertexCount);
}
// Convert highest index (including the base offset) into a count
DWORD dwHighestVertexCount = pDrawContext->dwBaseVertexIndex + pDrawContext->HighIndex + 1;
// Use the biggest vertex count that can be reached
if (pDrawContext->VerticesInBuffer < dwHighestVertexCount)
pDrawContext->VerticesInBuffer = dwHighestVertexCount;
// Convert the range of indices into a count
pDrawContext->NumVerticesToUse = pDrawContext->HighIndex - pDrawContext->LowIndex + 1;
}
else {
// If we are drawing from an offset, we know that the vertex count must have
// 'offset' vertices before the first drawn vertices
pDrawContext->NumVerticesToUse = pDrawContext->dwVertexCount;
}
// Get the number of streams

View File

@ -40,7 +40,7 @@ typedef struct _CxbxDrawContext
IN PWORD pXboxIndexData; // Set by D3DDevice_DrawIndexedVertices, D3DDevice_DrawIndexedVerticesUP and HLE_draw_inline_elements
IN DWORD dwBaseVertexIndex; // Set to g_Xbox_BaseVertexIndex in D3DDevice_DrawIndexedVertices
IN INDEX16 LowIndex, HighIndex; // Set when pXboxIndexData is set
IN size_t VerticesInBuffer; // Set by CxbxVertexBufferConverter::Apply
IN UINT NumVerticesToUse; // Set by CxbxVertexBufferConverter::Apply
// Data if Draw...UP call
IN PVOID pXboxVertexStreamZeroData;
IN UINT uiXboxVertexStreamZeroStride;