From 14091b03a46d50a771c091bc4e7a5b2d00bc8882 Mon Sep 17 00:00:00 2001 From: patrickvl Date: Mon, 21 Oct 2019 00:01:37 +0200 Subject: [PATCH 01/13] Speed up drawing indexed quads, by converting the indices (from ABCD quads to ABC+CDA triangles), and doing one call with those indices (instead of drawing each quad separately) --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 349 ++++++++++++---------- src/core/hle/D3D8/XbConvert.cpp | 6 +- src/core/hle/D3D8/XbVertexBuffer.cpp | 3 +- 3 files changed, 191 insertions(+), 167 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 6afc5c8da..4edb57fa7 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -2462,6 +2462,70 @@ static void EmuVerifyResourceIsRegistered(XTL::X_D3DResource *pResource, DWORD D CreateHostResource(pResource, D3DUsage, iTextureStage, dwSize); } +// TODO : Move to own file +constexpr UINT QuadToTriangleVertexCount(UINT NrOfQuadVertices) +{ + return (NrOfQuadVertices * VERTICES_PER_TRIANGLE * TRIANGLES_PER_QUAD) / VERTICES_PER_QUAD; +} + +// TODO : Move to own file +bool WindingClockwise = true; + +// TODO : Move to own file +void CxbxConvertQuadListToTriangleListIndices( + INDEX16* pIndexData, + unsigned NrOfTriangleVertices, + INDEX16* pQuadToTriangleIndexBuffer) +{ + unsigned i = 0; + while (i + (TRIANGLES_PER_QUAD * VERTICES_PER_TRIANGLE) < NrOfTriangleVertices) { + if (WindingClockwise) { + // ABCD becomes ABC+CDA, so this is triangle 1 : + pQuadToTriangleIndexBuffer[i + 0] = pIndexData[0]; // A + pQuadToTriangleIndexBuffer[i + 1] = pIndexData[1]; // B + pQuadToTriangleIndexBuffer[i + 2] = pIndexData[2]; // C + i += VERTICES_PER_TRIANGLE; + + // And this is triangle 2 : + pQuadToTriangleIndexBuffer[i + 0] = pIndexData[2]; // C + pQuadToTriangleIndexBuffer[i + 1] = pIndexData[3]; // D + pQuadToTriangleIndexBuffer[i + 2] = pIndexData[0]; // A + i += VERTICES_PER_TRIANGLE; + } + else { + // ABCD becomes ADC+CBA, so this is triangle 1 : + pQuadToTriangleIndexBuffer[i + 0] = pIndexData[0]; // A + pQuadToTriangleIndexBuffer[i + 1] = pIndexData[3]; // D + pQuadToTriangleIndexBuffer[i + 2] = pIndexData[2]; // C + i += VERTICES_PER_TRIANGLE; + + // And this is triangle 2 : + pQuadToTriangleIndexBuffer[i + 0] = pIndexData[2]; // C + pQuadToTriangleIndexBuffer[i + 1] = pIndexData[1]; // B + pQuadToTriangleIndexBuffer[i + 2] = pIndexData[0]; // A + i += VERTICES_PER_TRIANGLE; + } + + // Next quad, please : + pIndexData += VERTICES_PER_QUAD; + } +} + +// TODO : Move to own file +INDEX16* CxbxInitializeQuadListIndexBuffer(INDEX16* pIndexData, unsigned QuadVertexCount) +{ + UINT NrOfTriangleVertices = QuadToTriangleVertexCount(QuadVertexCount); + INDEX16* pQuadToTriangleIndexBuffer = (INDEX16*)malloc(sizeof(INDEX16) * NrOfTriangleVertices); + CxbxConvertQuadListToTriangleListIndices(pIndexData, NrOfTriangleVertices, pQuadToTriangleIndexBuffer); + return pQuadToTriangleIndexBuffer; +} + +// TODO : Move to own file +void CxbxReleaseQuadListIndexBuffer(void* pHostIndexData) +{ + free(pHostIndexData); +} + typedef struct { uint64_t Hash = 0; DWORD IndexCount = 0; @@ -2478,17 +2542,23 @@ void CxbxRemoveIndexBuffer(PWORD pData) void CxbxUpdateActiveIndexBuffer ( PWORD pIndexData, - UINT IndexCount + unsigned IndexCount, + bool bConvertQuadsToTriangles ) { LOG_INIT; // Allows use of DEBUG_D3DRESULT + // For now, indicate the quad-to-triangles case using the otherwise + // (due to alignment) always-zero least significant bit : + PWORD Key = (PWORD)((uintptr_t)pIndexData | (bConvertQuadsToTriangles ? 1 : 0)); + unsigned RequiredIndexCount = bConvertQuadsToTriangles ? QuadToTriangleVertexCount(IndexCount) : IndexCount; + // Create a reference to the active buffer - ConvertedIndexBuffer& indexBuffer = g_ConvertedIndexBuffers[pIndexData]; + ConvertedIndexBuffer& indexBuffer = g_ConvertedIndexBuffers[Key]; // If the size has changed, free the buffer so it will be re-created if (indexBuffer.pHostIndexBuffer != nullptr && - indexBuffer.IndexCount < IndexCount) { + indexBuffer.IndexCount < RequiredIndexCount) { indexBuffer.pHostIndexBuffer->Release(); indexBuffer = {}; } @@ -2504,10 +2574,11 @@ void CxbxUpdateActiveIndexBuffer // "Buffers created with D3DPOOL_DEFAULT that do not specify D3DUSAGE_WRITEONLY may suffer a severe performance penalty." const DWORD D3DUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY; // Was D3DUSAGE_WRITEONLY // Create a new native index buffer of the above determined size : + UINT uiIndexBufferSize = sizeof(INDEX16) * RequiredIndexCount; HRESULT hRet = g_pD3DDevice->CreateIndexBuffer( - IndexCount * 2, + uiIndexBufferSize, // Size of the index buffer, in bytes. D3DUsage, - D3DFMT_INDEX16, + /*Format=*/D3DFMT_INDEX16, D3DPool, &indexBuffer.pHostIndexBuffer , nullptr // pSharedHandle @@ -2519,10 +2590,10 @@ void CxbxUpdateActiveIndexBuffer } // If the data needs updating, do so - uint64_t uiHash = ComputeHash(pIndexData, IndexCount * 2); + uint64_t uiHash = ComputeHash(pIndexData, IndexCount * sizeof(INDEX16)); if (uiHash != indexBuffer.Hash) { // Update the Index Count and the hash - indexBuffer.IndexCount = IndexCount; + indexBuffer.IndexCount = RequiredIndexCount; indexBuffer.Hash = uiHash; // Update the host index buffer @@ -2532,8 +2603,13 @@ void CxbxUpdateActiveIndexBuffer CxbxKrnlCleanup("CxbxUpdateActiveIndexBuffer: Could not lock index buffer!"); } - EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Copying %d indices (D3DFMT_INDEX16)", IndexCount); - memcpy(pData, pIndexData, IndexCount * sizeof(INDEX16)); + if (bConvertQuadsToTriangles) { + EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Converting quads to %d triangle indices (D3DFMT_INDEX16)", RequiredIndexCount); + CxbxConvertQuadListToTriangleListIndices((INDEX16*)pIndexData, RequiredIndexCount, (INDEX16*)pData); + } else { + EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Copying %d indices (D3DFMT_INDEX16)", IndexCount); + memcpy(pData, pIndexData, IndexCount * sizeof(INDEX16)); + } indexBuffer.pHostIndexBuffer->Unlock(); } @@ -6392,13 +6468,6 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexShader) } // TODO : Move to own file -constexpr UINT QuadToTriangleVertexCount(UINT NrOfQuadVertices) -{ - return (NrOfQuadVertices * VERTICES_PER_TRIANGLE * TRIANGLES_PER_QUAD) / VERTICES_PER_QUAD; -} - -// TODO : Move to own file -bool WindingClockwise = true; constexpr unsigned int IndicesPerPage = PAGE_SIZE / sizeof(INDEX16); constexpr unsigned int InputQuadsPerPage = ((IndicesPerPage * VERTICES_PER_QUAD) / VERTICES_PER_TRIANGLE) / TRIANGLES_PER_QUAD; @@ -6418,33 +6487,33 @@ INDEX16 *CxbxAssureQuadListIndexBuffer(UINT NrOfQuadVertices) UINT i = 0; INDEX16 j = 0; - while (i + 6 < NrOfTriangleVertices) + while (i + (TRIANGLES_PER_QUAD * VERTICES_PER_TRIANGLE) < NrOfTriangleVertices) { if (WindingClockwise) { // ABCD becomes ABC+CDA, so this is triangle 1 : - pQuadToTriangleIndexBuffer[i + 0] = j + 0; - pQuadToTriangleIndexBuffer[i + 1] = j + 1; - pQuadToTriangleIndexBuffer[i + 2] = j + 2; + pQuadToTriangleIndexBuffer[i + 0] = j + 0; // A + pQuadToTriangleIndexBuffer[i + 1] = j + 1; // B + pQuadToTriangleIndexBuffer[i + 2] = j + 2; // C i += VERTICES_PER_TRIANGLE; // And this is triangle 2 : - pQuadToTriangleIndexBuffer[i + 0] = j + 2; - pQuadToTriangleIndexBuffer[i + 1] = j + 3; - pQuadToTriangleIndexBuffer[i + 2] = j + 0; + pQuadToTriangleIndexBuffer[i + 0] = j + 2; // C + pQuadToTriangleIndexBuffer[i + 1] = j + 3; // D + pQuadToTriangleIndexBuffer[i + 2] = j + 0; // A i += VERTICES_PER_TRIANGLE; } else { // ABCD becomes ADC+CBA, so this is triangle 1 : - pQuadToTriangleIndexBuffer[i + 0] = j + 0; - pQuadToTriangleIndexBuffer[i + 1] = j + 3; - pQuadToTriangleIndexBuffer[i + 2] = j + 2; + pQuadToTriangleIndexBuffer[i + 0] = j + 0; // A + pQuadToTriangleIndexBuffer[i + 1] = j + 3; // D + pQuadToTriangleIndexBuffer[i + 2] = j + 2; // C i += VERTICES_PER_TRIANGLE; // And this is triangle 2 : - pQuadToTriangleIndexBuffer[i + 0] = j + 2; - pQuadToTriangleIndexBuffer[i + 1] = j + 1; - pQuadToTriangleIndexBuffer[i + 2] = j + 0; + pQuadToTriangleIndexBuffer[i + 0] = j + 2; // C + pQuadToTriangleIndexBuffer[i + 1] = j + 1; // B + pQuadToTriangleIndexBuffer[i + 2] = j + 0; // A i += VERTICES_PER_TRIANGLE; } @@ -6590,74 +6659,44 @@ void CxbxDrawIndexed(CxbxDrawContext &DrawContext) assert(DrawContext.pIndexData != nullptr); assert(IsValidCurrentShader()); - CxbxUpdateActiveIndexBuffer(DrawContext.pIndexData, DrawContext.dwVertexCount); + bool bQuadListAsTriangleList = (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_QUADLIST); + CxbxUpdateActiveIndexBuffer(DrawContext.pIndexData, DrawContext.dwVertexCount, bQuadListAsTriangleList); VertexBufferConverter.Apply(&DrawContext); - if (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_QUADLIST) { - UINT uiStartIndex = 0; - int iNumVertices = (int)DrawContext.dwVertexCount; - // Indexed quadlist can be drawn using unpatched indexes via multiple draws of 2 'strip' triangles : - // 4 vertices are just enough for two triangles (a fan starts with 3 vertices for 1 triangle, - // and adds 1 triangle via 1 additional vertex) - // This is slower (because of call-overhead) but doesn't require any index buffer patching. - // Draw 1 quad as a 2 triangles in a fan (which both have the same winding order) : - // Test-cases : XDK samples reaching this case are : DisplacementMap, Ripple - // Test-case : XDK Samples (Billboard, BumpLens, DebugKeyboard, Gamepad, Lensflare, PerfTest?VolumeLight, PointSprites, Tiling, VolumeFog, VolumeSprites, etc) - while (iNumVertices >= VERTICES_PER_QUAD) { - INDEX16 LowIndex, HighIndex; - WalkIndexBuffer(LowIndex, HighIndex, &(DrawContext.pIndexData[uiStartIndex]), VERTICES_PER_QUAD); + //Walk through index buffer + // Determine highest and lowest index in use : + INDEX16 LowIndex, HighIndex; + WalkIndexBuffer(LowIndex, HighIndex, &(DrawContext.pIndexData[/*DrawContext.dwStartVertex=*/0]), DrawContext.dwVertexCount); - // Emulate a quad by drawing each as a fan of 2 triangles - HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitive( - D3DPT_TRIANGLEFAN, - DrawContext.dwIndexBase, - LowIndex, // minIndex - (HighIndex - LowIndex) + 1, // NumVertices - uiStartIndex, - TRIANGLES_PER_QUAD // primCount = Draw 2 triangles - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitive(X_D3DPT_QUADLIST)"); + D3DPRIMITIVETYPE PrimitiveType = bQuadListAsTriangleList ? D3DPT_TRIANGLELIST : EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType); + UINT primCount = (bQuadListAsTriangleList ? TRIANGLES_PER_QUAD : 1) * DrawContext.dwHostPrimitiveCount; + HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitive( + PrimitiveType, + /* BaseVertexIndex = */DrawContext.dwIndexBase, + /* MinVertexIndex = */LowIndex, + /* NumVertices = */(HighIndex - LowIndex) + 1,//using index vertex span here. // TODO : g_EmuD3DActiveStreamSizes[0], // Note : ATI drivers are especially picky about this - + // NumVertices should be the span of covered vertices in the active vertex buffer (TODO : Is stream 0 correct?) + /* startIndex = DrawContext.dwStartVertex = */0, + primCount); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitive"); - uiStartIndex += VERTICES_PER_QUAD; - iNumVertices -= VERTICES_PER_QUAD; - g_dwPrimPerFrame += TRIANGLES_PER_QUAD; + g_dwPrimPerFrame += primCount; + if (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_LINELOOP) { + // Close line-loops using a final single line, drawn from the end to the start vertex + LOG_TEST_CASE("X_D3DPT_LINELOOP"); + // Read the end and start index from the supplied index data + LowIndex = DrawContext.pIndexData[0]; + HighIndex = DrawContext.pIndexData[DrawContext.dwHostPrimitiveCount]; + // If needed, swap so highest index is higher than lowest (duh) + if (HighIndex < LowIndex) { + HighIndex ^= LowIndex; + LowIndex ^= HighIndex; + HighIndex ^= LowIndex; } - } - else { - //Walk through index buffer - // Determine highest and lowest index in use : - INDEX16 LowIndex, HighIndex; - WalkIndexBuffer(LowIndex, HighIndex, &(DrawContext.pIndexData[DrawContext.dwStartVertex]), DrawContext.dwVertexCount); - // Primitives other than X_D3DPT_QUADLIST can be drawn using one DrawIndexedPrimitive call : - HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitive( - EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), - DrawContext.dwIndexBase, - /* MinVertexIndex = */LowIndex, - /* NumVertices = */(HighIndex - LowIndex) + 1,//using index vertex span here. // TODO : g_EmuD3DActiveStreamSizes[0], // Note : ATI drivers are especially picky about this - - // NumVertices should be the span of covered vertices in the active vertex buffer (TODO : Is stream 0 correct?) - DrawContext.dwStartVertex, - DrawContext.dwHostPrimitiveCount); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitive"); - - g_dwPrimPerFrame += DrawContext.dwHostPrimitiveCount; - if (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_LINELOOP) { - // Close line-loops using a final single line, drawn from the end to the start vertex - LOG_TEST_CASE("X_D3DPT_LINELOOP"); - // Read the end and start index from the supplied index data - LowIndex = DrawContext.pIndexData[0]; - HighIndex = DrawContext.pIndexData[DrawContext.dwHostPrimitiveCount]; - // If needed, swap so highest index is higher than lowest (duh) - if (HighIndex < LowIndex) { - HighIndex ^= LowIndex; - LowIndex ^= HighIndex; - HighIndex ^= LowIndex; - } - - // Draw the closing line using a helper function (which will SetIndices) - CxbxDrawIndexedClosingLine(LowIndex, HighIndex); - // NOTE : We don't restore the previously active index buffer - } + // Draw the closing line using a helper function (which will SetIndices) + CxbxDrawIndexedClosingLine(LowIndex, HighIndex); + // NOTE : We don't restore the previously active index buffer } } @@ -6687,12 +6726,12 @@ void CxbxDrawPrimitiveUP(CxbxDrawContext &DrawContext) INDEX16 HighIndex = (INDEX16)(DrawContext.dwVertexCount - 1); HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( - D3DPT_TRIANGLELIST, // Draw indexed triangles instead of quads - LowIndex, // MinVertexIndex - (HighIndex - LowIndex) + 1, // NumVertexIndices + /*PrimitiveType=*/D3DPT_TRIANGLELIST, // Draw indexed triangles instead of quads + /*MinVertexIndex=*/LowIndex, + /*NumVertexIndices=*/(HighIndex - LowIndex) + 1, PrimitiveCount, pIndexData, - D3DFMT_INDEX16, // IndexDataFormat + /*IndexDataFormat=*/D3DFMT_INDEX16, DrawContext.pHostVertexStreamZeroData, DrawContext.uiHostVertexStreamZeroStride ); @@ -6927,7 +6966,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices) INDEX16 HighIndex = LowIndex + (INDEX16)DrawContext.dwVertexCount - 1; // Emulate a quad by drawing each as a fan of 2 triangles HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitive( - D3DPT_TRIANGLELIST, // Draw indexed triangles instead of quads + /*PrimitiveType=*/D3DPT_TRIANGLELIST, // Draw indexed triangles instead of quads 0, // BaseVertexIndex LowIndex, // minIndex (HighIndex - LowIndex) + 1, // NumVertices, @@ -7046,8 +7085,6 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVertices) CxbxUpdateNativeD3DResources(); if (IsValidCurrentShader()) { - CxbxUpdateActiveIndexBuffer(pIndexData, VertexCount); - CxbxDrawContext DrawContext = {}; DrawContext.XboxPrimitiveType = PrimitiveType; @@ -7110,80 +7147,62 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) // Don't set DrawContext.pIndexData = (INDEX16*)pIndexData; // Used by GetVerticesInBuffer VertexBufferConverter.Apply(&DrawContext); - if (DrawContext.XboxPrimitiveType == X_D3DPT_QUADLIST) { - // Indexed quadlist can be drawn using unpatched indexes via multiple draws of 2 'strip' triangles : - // Those 4 vertices are just enough for two triangles (a fan starts with 3 vertices for 1 triangle, - // and adds 1 triangle via 1 additional vertex) - // This is slower (because of call-overhead) but doesn't require any index buffer patching - // Draw 1 quad as a 2 triangles in a fan (which both have the same winding order) : - // LOG_TEST_CASE("X_D3DPT_QUADLIST"); // Test-case : Buffy: The Vampire Slayer, FastLoad XDK Sample - INDEX16* pWalkIndexData = (INDEX16*)pIndexData; - int iNumVertices = (int)VertexCount; - while (iNumVertices >= VERTICES_PER_QUAD) { - // Determine highest and lowest index in use : - INDEX16 LowIndex; - INDEX16 HighIndex; - WalkIndexBuffer(LowIndex, HighIndex, pWalkIndexData, VERTICES_PER_QUAD); - // Emulate a quad by drawing each as a fan of 2 triangles - HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( - D3DPT_TRIANGLEFAN, // Draw a triangle-fan instead of a quad - LowIndex, // MinVertexIndex - (HighIndex - LowIndex) + 1, // NumVertexIndices - TRIANGLES_PER_QUAD, // primCount = Draw 2 triangles - pWalkIndexData, - D3DFMT_INDEX16, // IndexDataFormat - DrawContext.pHostVertexStreamZeroData, - DrawContext.uiHostVertexStreamZeroStride - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitiveUP(X_D3DPT_QUADLIST)"); + // Walk through the index buffer + INDEX16 LowIndex, HighIndex; + WalkIndexBuffer(LowIndex, HighIndex, (INDEX16*)pIndexData, DrawContext.dwVertexCount); - pWalkIndexData += VERTICES_PER_QUAD; - iNumVertices -= VERTICES_PER_QUAD; + INDEX16* pHostIndexData; + bool bQuadListAsTriangleList = DrawContext.XboxPrimitiveType == X_D3DPT_QUADLIST; + if (bQuadListAsTriangleList) { + LOG_TEST_CASE("X_D3DPT_QUADLIST"); // Test-case : Buffy: The Vampire Slayer, FastLoad XDK Sample + pHostIndexData = CxbxInitializeQuadListIndexBuffer((INDEX16 *)pIndexData, VertexCount); + // Note, that LowIndex and HighIndex won't change due to this quad-to-triangle conversion, + // so it's less work to WalkIndexBuffer over the input instead of the converted index buffer. + } else { + // LOG_TEST_CASE("DrawIndexedPrimitiveUP"); // Test-case : Burnout, Namco Museum 50th Anniversary + pHostIndexData = (INDEX16 *)pIndexData; + } + + D3DPRIMITIVETYPE PrimitiveType = bQuadListAsTriangleList ? D3DPT_TRIANGLELIST : EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType); + UINT PrimitiveCount = (bQuadListAsTriangleList ? TRIANGLES_PER_QUAD : 1) * DrawContext.dwHostPrimitiveCount; + HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( + PrimitiveType, + /*MinVertexIndex=*/LowIndex, + /*NumVertexIndices=*/(HighIndex - LowIndex) + 1, //this shall be Vertex Spans DrawContext.dwVertexCount, + PrimitiveCount, + (const void *)pHostIndexData, + /*IndexDataFormat=*/D3DFMT_INDEX16, + DrawContext.pHostVertexStreamZeroData, + DrawContext.uiHostVertexStreamZeroStride + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitiveUP"); + + if (bQuadListAsTriangleList) { + CxbxReleaseQuadListIndexBuffer(pHostIndexData); + } + + g_dwPrimPerFrame += DrawContext.dwHostPrimitiveCount; + if (DrawContext.XboxPrimitiveType == X_D3DPT_LINELOOP) { + // Close line-loops using a final single line, drawn from the end to the start vertex + LOG_TEST_CASE("X_D3DPT_LINELOOP"); // TODO : Which titles reach this case? + // Read the end and start index from the supplied index data + LowIndex = ((INDEX16*)pIndexData)[0]; + HighIndex = ((INDEX16*)pIndexData)[DrawContext.dwHostPrimitiveCount]; + // If needed, swap so highest index is higher than lowest (duh) + if (HighIndex < LowIndex) { + HighIndex ^= LowIndex; + LowIndex ^= HighIndex; + HighIndex ^= LowIndex; } - g_dwPrimPerFrame += VertexCount / VERTICES_PER_QUAD * TRIANGLES_PER_QUAD; - } - else { - // Walk through the index buffer - INDEX16 LowIndex, HighIndex; - WalkIndexBuffer(LowIndex, HighIndex, (INDEX16*)pIndexData, DrawContext.dwVertexCount); - - // LOG_TEST_CASE("DrawIndexedPrimitiveUP"); // Test-case : Burnout, Namco Museum 50th Anniversary - HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( - EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), - LowIndex, // MinVertexIndex - (HighIndex - LowIndex) + 1, //this shall be Vertex Spans DrawContext.dwVertexCount, // NumVertexIndices - DrawContext.dwHostPrimitiveCount, - pIndexData, - D3DFMT_INDEX16, // IndexDataFormat + // Close line-loops using a final single line, drawn from the end to the start vertex : + CxbxDrawIndexedClosingLineUP( + LowIndex, + HighIndex, DrawContext.pHostVertexStreamZeroData, DrawContext.uiHostVertexStreamZeroStride ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitiveUP"); - - g_dwPrimPerFrame += DrawContext.dwHostPrimitiveCount; - if (DrawContext.XboxPrimitiveType == X_D3DPT_LINELOOP) { - // Close line-loops using a final single line, drawn from the end to the start vertex - LOG_TEST_CASE("X_D3DPT_LINELOOP"); // TODO : Which titles reach this case? - // Read the end and start index from the supplied index data - LowIndex = ((INDEX16*)pIndexData)[0]; - HighIndex = ((INDEX16*)pIndexData)[DrawContext.dwHostPrimitiveCount]; - // If needed, swap so highest index is higher than lowest (duh) - if (HighIndex < LowIndex) { - HighIndex ^= LowIndex; - LowIndex ^= HighIndex; - HighIndex ^= LowIndex; - } - - // Close line-loops using a final single line, drawn from the end to the start vertex : - CxbxDrawIndexedClosingLineUP( - LowIndex, - HighIndex, - DrawContext.pHostVertexStreamZeroData, - DrawContext.uiHostVertexStreamZeroStride - ); - } } } diff --git a/src/core/hle/D3D8/XbConvert.cpp b/src/core/hle/D3D8/XbConvert.cpp index 67a1de169..cde765777 100644 --- a/src/core/hle/D3D8/XbConvert.cpp +++ b/src/core/hle/D3D8/XbConvert.cpp @@ -1182,7 +1182,11 @@ D3DMULTISAMPLE_TYPE EmuXB2PC_D3DMultiSampleFormat(DWORD Type) // lookup table for converting vertex count to primitive count UINT EmuD3DVertexToPrimitive[11][2] = -{ +{ + // First number the number of vertices per primitive + // Second number is the starting number of vertices the draw requires + // Example : Triangle list, has no starting vertices, and uses 3 vertices for each triangle + // Example : Triangle strip, starts with 2 vertices, and adds 1 for each triangle {0, 0}, // NULL {1, 0}, // X_D3DPT_POINTLIST {2, 0}, // X_D3DPT_LINELIST diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index 6efcd71e2..ae28802a3 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -125,7 +125,8 @@ size_t GetVerticesInBuffer(DWORD dwOffset, DWORD dwVertexCount, PWORD pIndexData } // We are an indexed draw, so we have to parse the index buffer - // The highest index we see can be used to determine the vertex buffer size + // The highest index we see can be used to determine the vertex buffer size + // TODO : Should we use WalkIndexBuffer() instead? DWORD highestVertexIndex = 0; for (DWORD i = 0; i < dwVertexCount; i++) { if (highestVertexIndex < pIndexData[i]) { From a26560a8eef1a7097f404c0c69108d6184c4872a Mon Sep 17 00:00:00 2001 From: patrickvl Date: Mon, 21 Oct 2019 00:15:12 +0200 Subject: [PATCH 02/13] Documented a few more known test-cases --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 2 ++ src/core/hle/D3D8/XbVertexShader.cpp | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 4edb57fa7..4b9fe3950 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -6949,8 +6949,10 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices) if (StartVertex > 0) { LOG_TEST_CASE("X_D3DPT_QUADLIST StartVertex > 0"); // test-case : BLiNX: the time sweeper + // test-case : Call of Duty: Finest Hour // test-case : Halo - Combat Evolved // test-case : Worms 3D Special Edition + // test-case : Tony Hawk's Pro Skater 2X // test-case : XDK sample Lensflare DrawContext.dwStartVertex = StartVertex; // Breakpoint location for testing. } diff --git a/src/core/hle/D3D8/XbVertexShader.cpp b/src/core/hle/D3D8/XbVertexShader.cpp index c15862b49..077ec8741 100644 --- a/src/core/hle/D3D8/XbVertexShader.cpp +++ b/src/core/hle/D3D8/XbVertexShader.cpp @@ -1813,7 +1813,8 @@ private: { DWORD regNum = (XboxToken & X_D3DVSD_VERTEXREGMASK) >> X_D3DVSD_VERTEXREGSHIFT; if (regNum >= hostTemporaryRegisterCount /*12 for D3D8, D3D9 value depends on host GPU */) { - // Lego Star Wars hits this + // test-case : BLiNX: the time sweeper + // test-case : Lego Star Wars LOG_TEST_CASE("RegNum > NumTemps"); } return regNum; From fedd9fbf67f2284eb10a6c4e86ba4a654d9505a6 Mon Sep 17 00:00:00 2001 From: patrickvl Date: Mon, 21 Oct 2019 00:55:08 +0200 Subject: [PATCH 03/13] Cleaned up and expanded upon commented test-case --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 4 ++-- src/core/hle/D3D8/XbVertexBuffer.cpp | 5 +++-- src/core/hle/D3D8/XbVertexShader.cpp | 7 ++++++- src/core/hle/Intercept.cpp | 3 +-- src/devices/x86/EmuX86.cpp | 2 +- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 4b9fe3950..efd27ee20 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -6714,7 +6714,7 @@ void CxbxDrawPrimitiveUP(CxbxDrawContext &DrawContext) VertexBufferConverter.Apply(&DrawContext); if (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_QUADLIST) { - // LOG_TEST_CASE("X_D3DPT_QUADLIST"); // X-Marbles and XDK Sample PlayField hits this case + // LOG_TEST_CASE("X_D3DPT_QUADLIST"); // test-case : X-Marbles and XDK Sample PlayField // Draw quadlists using a single 'quad-to-triangle mapping' index buffer : INDEX16 *pIndexData = CxbxAssureQuadListIndexBuffer(DrawContext.dwVertexCount); // Convert quad vertex-count to triangle vertex count : @@ -6945,7 +6945,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices) VertexBufferConverter.Apply(&DrawContext); if (DrawContext.XboxPrimitiveType == X_D3DPT_QUADLIST) { - // LOG_TEST_CASE("X_D3DPT_QUADLIST"); // ?X-Marbles and XDK Sample (Cartoon, ?maybe PlayField?) hits this case + // LOG_TEST_CASE("X_D3DPT_QUADLIST"); // test-case : ?X-Marbles and XDK Sample (Cartoon, ?maybe PlayField?) if (StartVertex > 0) { LOG_TEST_CASE("X_D3DPT_QUADLIST StartVertex > 0"); // test-case : BLiNX: the time sweeper diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index ae28802a3..3aa559b48 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -84,7 +84,7 @@ void CxbxPatchedStream::Activate(CxbxDrawContext *pDrawContext, UINT uiStream) c //DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetStreamSource"); if (FAILED(hRet)) { CxbxKrnlCleanup("Failed to set the type patched buffer as the new stream source!\n"); - // TODO : Cartoon hits the above case when the vertex cache size is 0. + // TODO : test-case : XDK Cartoon hits the above case when the vertex cache size is 0. } } } @@ -631,7 +631,8 @@ void CxbxVertexBufferConverter::ConvertStream break; } case XTL::X_D3DVSDT_NONE: { // 0x02: - // Test-case : WWE RAW2 + // Test-case : WWE RAW2 + // Test-case : PetitCopter LOG_TEST_CASE("X_D3DVSDT_NONE"); // No host element data (but Xbox size can be above zero, when used for X_D3DVSD_MASK_SKIP* break; diff --git a/src/core/hle/D3D8/XbVertexShader.cpp b/src/core/hle/D3D8/XbVertexShader.cpp index 077ec8741..a4a005234 100644 --- a/src/core/hle/D3D8/XbVertexShader.cpp +++ b/src/core/hle/D3D8/XbVertexShader.cpp @@ -911,6 +911,11 @@ static void VshWriteShader(VSH_XBOX_SHADER *pShader, // We count down from the highest available on the host because Xbox titles don't use values that high, and we read from c192 (one above maximum Xbox c191 constant) and up static int temporaryRegisterBase = g_D3DCaps.VS20Caps.NumTemps - 13; moveConstantsToTemporaries << "mov r" << (temporaryRegisterBase + i) << ", c" << (CXBX_D3DVS_CONSTREG_VERTEXDATA4F_BASE + i) << "\n"; + // test-case : Blade II (before menu's) + // test-case : Namco Museum 50th Anniversary (at boot) + // test-case : Pac-Man World 2 (at boot) + // test-case : The Simpsons Road Rage (leaving menu's, before entering in-game) + // test-case : The SpongeBob SquarePants Movie (before menu's) LOG_TEST_CASE("Shader uses undeclared Vertex Input Registers"); i++; continue; @@ -1824,7 +1829,7 @@ private: { DWORD regNum = (XboxToken & X_D3DVSD_VERTEXREGINMASK) >> X_D3DVSD_VERTEXREGINSHIFT; if (regNum >= hostTemporaryRegisterCount /*12 for D3D8, D3D9 value depends on host GPU */) { - // Lego Star Wars hits this + // test-case : Lego Star Wars LOG_TEST_CASE("RegNum > NumTemps"); } return regNum; diff --git a/src/core/hle/Intercept.cpp b/src/core/hle/Intercept.cpp index ee2f60c52..392ae0e7e 100644 --- a/src/core/hle/Intercept.cpp +++ b/src/core/hle/Intercept.cpp @@ -147,10 +147,9 @@ bool VerifySymbolAddressAgainstXRef(char *SymbolName, xbaddr Address, int XRef) return true; } - // For XREF_D3DTSS_TEXCOORDINDEX, Kabuki Warriors hits this case CxbxPopupMessage(LOG_LEVEL::WARNING, CxbxMsgDlgIcon_Warn, "Verification of %s failed : XREF was 0x%.8X while lookup gave 0x%.8X", SymbolName, XRefAddr, Address); - // For XREF_D3DTSS_TEXCOORDINDEX, Kabuki Warriors hits this case + // this case : Kabuki Warriors (for XREF_D3DTSS_TEXCOORDINDEX) return false; }*/ diff --git a/src/devices/x86/EmuX86.cpp b/src/devices/x86/EmuX86.cpp index 5a96d8908..cf7ec8f64 100644 --- a/src/devices/x86/EmuX86.cpp +++ b/src/devices/x86/EmuX86.cpp @@ -3282,7 +3282,7 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e) // We do not emulate processor specific registers just yet // Some titles attempt to manually set the TSC via this instruction // This needs fixing eventually, but should be acceptible to ignore for now! - // Chase: Hollywood Stunt Driver hits this + // test-case : Chase: Hollywood Stunt Driver EmuLog(LOG_LEVEL::WARNING, "WRMSR instruction ignored"); break; case I_XOR: From fce300d25a317046603bedc347ba03b9c6a633b9 Mon Sep 17 00:00:00 2001 From: patrickvl Date: Mon, 21 Oct 2019 01:38:23 +0200 Subject: [PATCH 04/13] Small fixes, but not for the regression, yet. --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index efd27ee20..b1b5147a9 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -6668,10 +6668,9 @@ void CxbxDrawIndexed(CxbxDrawContext &DrawContext) INDEX16 LowIndex, HighIndex; WalkIndexBuffer(LowIndex, HighIndex, &(DrawContext.pIndexData[/*DrawContext.dwStartVertex=*/0]), DrawContext.dwVertexCount); - D3DPRIMITIVETYPE PrimitiveType = bQuadListAsTriangleList ? D3DPT_TRIANGLELIST : EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType); UINT primCount = (bQuadListAsTriangleList ? TRIANGLES_PER_QUAD : 1) * DrawContext.dwHostPrimitiveCount; HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitive( - PrimitiveType, + /* PrimitiveType = */EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), /* BaseVertexIndex = */DrawContext.dwIndexBase, /* MinVertexIndex = */LowIndex, /* NumVertices = */(HighIndex - LowIndex) + 1,//using index vertex span here. // TODO : g_EmuD3DActiveStreamSizes[0], // Note : ATI drivers are especially picky about this - @@ -7166,10 +7165,9 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) pHostIndexData = (INDEX16 *)pIndexData; } - D3DPRIMITIVETYPE PrimitiveType = bQuadListAsTriangleList ? D3DPT_TRIANGLELIST : EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType); UINT PrimitiveCount = (bQuadListAsTriangleList ? TRIANGLES_PER_QUAD : 1) * DrawContext.dwHostPrimitiveCount; HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( - PrimitiveType, + /*PrimitiveType=*/EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), /*MinVertexIndex=*/LowIndex, /*NumVertexIndices=*/(HighIndex - LowIndex) + 1, //this shall be Vertex Spans DrawContext.dwVertexCount, PrimitiveCount, @@ -7184,7 +7182,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) CxbxReleaseQuadListIndexBuffer(pHostIndexData); } - g_dwPrimPerFrame += DrawContext.dwHostPrimitiveCount; + g_dwPrimPerFrame += PrimitiveCount; if (DrawContext.XboxPrimitiveType == X_D3DPT_LINELOOP) { // Close line-loops using a final single line, drawn from the end to the start vertex LOG_TEST_CASE("X_D3DPT_LINELOOP"); // TODO : Which titles reach this case? From 3d58eba6b814bcbe3ae4e23666fb070f5b187c09 Mon Sep 17 00:00:00 2001 From: patrickvl Date: Mon, 21 Oct 2019 17:33:17 +0200 Subject: [PATCH 05/13] Cleanup, additional testcases and one fix for a corner-case (closing line-loops should take BaseVertexIndex into account) --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 200 +++++++++++++--------- src/core/hle/D3D8/XbConvert.h | 2 + 2 files changed, 117 insertions(+), 85 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index b1b5147a9..697f04c94 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -142,9 +142,6 @@ typedef uint64_t resource_key_t; extern void UpdateFPSCounter(); -// current active index buffer -static DWORD g_dwBaseVertexIndex = 0;// current active index buffer base index - // current active vertex stream static XTL::X_D3DVertexBuffer *g_pVertexBuffer = NULL; // current active vertex buffer static IDirect3DVertexBuffer *g_pDummyBuffer = NULL; // Dummy buffer, used to set unused stream sources with @@ -177,7 +174,7 @@ static XTL::X_D3DSurface *g_pXboxDepthStencil = NULL; static DWORD g_dwVertexShaderUsage = 0; // TODO : Move to XbVertexShader.cpp static DWORD g_VertexShaderSlots[X_VSH_MAX_INSTRUCTION_COUNT]; -DWORD g_XboxBaseVertexIndex = 0; +DWORD g_XboxBaseVertexIndex = 0; // a value that's effectively added to every VB Index stored in the index buffer DWORD g_DefaultPresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; DWORD g_PresentationIntervalOverride = 0; bool g_UnlockFramerateHack = false; // ignore the xbox presentation interval @@ -2468,46 +2465,46 @@ constexpr UINT QuadToTriangleVertexCount(UINT NrOfQuadVertices) return (NrOfQuadVertices * VERTICES_PER_TRIANGLE * TRIANGLES_PER_QUAD) / VERTICES_PER_QUAD; } -// TODO : Move to own file -bool WindingClockwise = true; +// TODO : Move to own file (or argument of all users) +bool bUseClockWiseWindingOrder = true; // TODO : Should this be fetched from X_D3DRS_FRONTFACE (or X_D3DRS_CULLMODE)? // TODO : Move to own file void CxbxConvertQuadListToTriangleListIndices( - INDEX16* pIndexData, - unsigned NrOfTriangleVertices, - INDEX16* pQuadToTriangleIndexBuffer) + INDEX16* pQuadIndexData, + unsigned uNrOfTriangleIndices, + INDEX16* pTriangleIndexData) { unsigned i = 0; - while (i + (TRIANGLES_PER_QUAD * VERTICES_PER_TRIANGLE) < NrOfTriangleVertices) { - if (WindingClockwise) { + while (i + (TRIANGLES_PER_QUAD * VERTICES_PER_TRIANGLE) < uNrOfTriangleIndices) { + if (bUseClockWiseWindingOrder) { // ABCD becomes ABC+CDA, so this is triangle 1 : - pQuadToTriangleIndexBuffer[i + 0] = pIndexData[0]; // A - pQuadToTriangleIndexBuffer[i + 1] = pIndexData[1]; // B - pQuadToTriangleIndexBuffer[i + 2] = pIndexData[2]; // C + pTriangleIndexData[i + 0] = pQuadIndexData[0]; // A + pTriangleIndexData[i + 1] = pQuadIndexData[1]; // B + pTriangleIndexData[i + 2] = pQuadIndexData[2]; // C i += VERTICES_PER_TRIANGLE; // And this is triangle 2 : - pQuadToTriangleIndexBuffer[i + 0] = pIndexData[2]; // C - pQuadToTriangleIndexBuffer[i + 1] = pIndexData[3]; // D - pQuadToTriangleIndexBuffer[i + 2] = pIndexData[0]; // A + pTriangleIndexData[i + 0] = pQuadIndexData[2]; // C + pTriangleIndexData[i + 1] = pQuadIndexData[3]; // D + pTriangleIndexData[i + 2] = pQuadIndexData[0]; // A i += VERTICES_PER_TRIANGLE; } else { // ABCD becomes ADC+CBA, so this is triangle 1 : - pQuadToTriangleIndexBuffer[i + 0] = pIndexData[0]; // A - pQuadToTriangleIndexBuffer[i + 1] = pIndexData[3]; // D - pQuadToTriangleIndexBuffer[i + 2] = pIndexData[2]; // C + pTriangleIndexData[i + 0] = pQuadIndexData[0]; // A + pTriangleIndexData[i + 1] = pQuadIndexData[3]; // D + pTriangleIndexData[i + 2] = pQuadIndexData[2]; // C i += VERTICES_PER_TRIANGLE; // And this is triangle 2 : - pQuadToTriangleIndexBuffer[i + 0] = pIndexData[2]; // C - pQuadToTriangleIndexBuffer[i + 1] = pIndexData[1]; // B - pQuadToTriangleIndexBuffer[i + 2] = pIndexData[0]; // A + pTriangleIndexData[i + 0] = pQuadIndexData[2]; // C + pTriangleIndexData[i + 1] = pQuadIndexData[1]; // B + pTriangleIndexData[i + 2] = pQuadIndexData[0]; // A i += VERTICES_PER_TRIANGLE; } // Next quad, please : - pIndexData += VERTICES_PER_QUAD; + pQuadIndexData += VERTICES_PER_QUAD; } } @@ -2530,36 +2527,46 @@ typedef struct { uint64_t Hash = 0; DWORD IndexCount = 0; IDirect3DIndexBuffer* pHostIndexBuffer = nullptr; + INDEX16 LowIndex; + INDEX16 HighIndex; } ConvertedIndexBuffer; -std::unordered_map g_ConvertedIndexBuffers; +std::unordered_map g_ConvertedIndexBuffers; void CxbxRemoveIndexBuffer(PWORD pData) { // HACK: Never Free } -void CxbxUpdateActiveIndexBuffer +ConvertedIndexBuffer& CxbxUpdateActiveIndexBuffer ( - PWORD pIndexData, - unsigned IndexCount, - bool bConvertQuadsToTriangles + INDEX16* pIndexData, + unsigned IndexCount, + bool bConvertQuadListToTriangleList ) { LOG_INIT; // Allows use of DEBUG_D3DRESULT - // For now, indicate the quad-to-triangles case using the otherwise - // (due to alignment) always-zero least significant bit : - PWORD Key = (PWORD)((uintptr_t)pIndexData | (bConvertQuadsToTriangles ? 1 : 0)); - unsigned RequiredIndexCount = bConvertQuadsToTriangles ? QuadToTriangleVertexCount(IndexCount) : IndexCount; + uint32_t LookupKey = (uint32_t)pIndexData; + unsigned RequiredIndexCount = IndexCount; + + if (bConvertQuadListToTriangleList) { + LOG_TEST_CASE("bConvertQuadListToTriangleList"); + RequiredIndexCount = QuadToTriangleVertexCount(IndexCount); + // For now, indicate the quad-to-triangles case using the otherwise + // (due to alignment) always-zero least significant bit : + LookupKey |= 1; + } // Create a reference to the active buffer - ConvertedIndexBuffer& indexBuffer = g_ConvertedIndexBuffers[Key]; + ConvertedIndexBuffer& indexBuffer = g_ConvertedIndexBuffers[LookupKey]; + + // If the current index buffer size is too small, free it, so it'll get re-created + bool bNeedRepopulation = indexBuffer.IndexCount < RequiredIndexCount; + if (bNeedRepopulation) { + if (indexBuffer.pHostIndexBuffer != nullptr) + indexBuffer.pHostIndexBuffer->Release(); - // If the size has changed, free the buffer so it will be re-created - if (indexBuffer.pHostIndexBuffer != nullptr && - indexBuffer.IndexCount < RequiredIndexCount) { - indexBuffer.pHostIndexBuffer->Release(); indexBuffer = {}; } @@ -2580,35 +2587,42 @@ void CxbxUpdateActiveIndexBuffer D3DUsage, /*Format=*/D3DFMT_INDEX16, D3DPool, - &indexBuffer.pHostIndexBuffer - , nullptr // pSharedHandle + &indexBuffer.pHostIndexBuffer, + nullptr // pSharedHandle ); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateIndexBuffer"); - if (FAILED(hRet)) CxbxKrnlCleanup("CxbxUpdateActiveIndexBuffer: IndexBuffer Create Failed!"); } - // If the data needs updating, do so + // TODO : Speeds this up, perhaps by hashing less often, and/or by + // doing two hashes : a small subset regularly, all data less frequently. uint64_t uiHash = ComputeHash(pIndexData, IndexCount * sizeof(INDEX16)); - if (uiHash != indexBuffer.Hash) { + + // If the data needs updating, do so + bNeedRepopulation |= (uiHash != indexBuffer.Hash); + if (bNeedRepopulation) { // Update the Index Count and the hash indexBuffer.IndexCount = RequiredIndexCount; indexBuffer.Hash = uiHash; // Update the host index buffer - D3DLockData* pData = nullptr; - indexBuffer.pHostIndexBuffer->Lock(0, 0, &pData, D3DLOCK_DISCARD); - if (pData == nullptr) { + D3DLockData* pHostIndexBufferData = nullptr; + indexBuffer.pHostIndexBuffer->Lock(0, 0, &pHostIndexBufferData, D3DLOCK_DISCARD); + if (pHostIndexBufferData == nullptr) { CxbxKrnlCleanup("CxbxUpdateActiveIndexBuffer: Could not lock index buffer!"); } - if (bConvertQuadsToTriangles) { + // Determine highest and lowest index in use : + WalkIndexBuffer(indexBuffer.LowIndex, indexBuffer.HighIndex, pIndexData, IndexCount); + if (bConvertQuadListToTriangleList) { + // Note, that LowIndex and HighIndex won't change due to any quad-to-triangle conversion, + // so it's less work to WalkIndexBuffer over the input instead of the converted index buffer. EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Converting quads to %d triangle indices (D3DFMT_INDEX16)", RequiredIndexCount); - CxbxConvertQuadListToTriangleListIndices((INDEX16*)pIndexData, RequiredIndexCount, (INDEX16*)pData); + CxbxConvertQuadListToTriangleListIndices((INDEX16*)pIndexData, RequiredIndexCount, (INDEX16*)pHostIndexBufferData); } else { EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Copying %d indices (D3DFMT_INDEX16)", IndexCount); - memcpy(pData, pIndexData, IndexCount * sizeof(INDEX16)); + memcpy(pHostIndexBufferData, pIndexData, IndexCount * sizeof(INDEX16)); } indexBuffer.pHostIndexBuffer->Unlock(); @@ -2621,6 +2635,8 @@ void CxbxUpdateActiveIndexBuffer if (FAILED(hRet)) CxbxKrnlCleanup("CxbxUpdateActiveIndexBuffer: SetIndices Failed!"); + + return indexBuffer; } void Direct3D_CreateDevice_Start @@ -6489,7 +6505,7 @@ INDEX16 *CxbxAssureQuadListIndexBuffer(UINT NrOfQuadVertices) INDEX16 j = 0; while (i + (TRIANGLES_PER_QUAD * VERTICES_PER_TRIANGLE) < NrOfTriangleVertices) { - if (WindingClockwise) { + if (bUseClockWiseWindingOrder) { // ABCD becomes ABC+CDA, so this is triangle 1 : pQuadToTriangleIndexBuffer[i + 0] = j + 0; // A pQuadToTriangleIndexBuffer[i + 1] = j + 1; // B @@ -6587,7 +6603,7 @@ void CxbxDrawIndexedClosingLine(INDEX16 LowIndex, INDEX16 HighIndex) HRESULT hRet; - const UINT uiIndexBufferSize = sizeof(INDEX16) * 2; // 4 bytes needed for 2 indices + const UINT uiIndexBufferSize = sizeof(INDEX16) * VERTICES_PER_LINE; // 4 bytes needed for 2 indices if (pClosingLineLoopIndexBuffer == nullptr) { hRet = g_pD3DDevice->CreateIndexBuffer( uiIndexBufferSize, @@ -6605,6 +6621,7 @@ void CxbxDrawIndexedClosingLine(INDEX16 LowIndex, INDEX16 HighIndex) hRet = pClosingLineLoopIndexBuffer->Lock(0, uiIndexBufferSize, (D3DLockData **)(&pCxbxClosingLineLoopIndexBufferData), D3DLOCK_DISCARD); DEBUG_D3DRESULT(hRet, "pClosingLineLoopIndexBuffer->Lock"); + // Set the indices for the two VERTICES_PER_LINE : pCxbxClosingLineLoopIndexBufferData[0] = LowIndex; pCxbxClosingLineLoopIndexBufferData[1] = HighIndex; @@ -6615,12 +6632,12 @@ void CxbxDrawIndexedClosingLine(INDEX16 LowIndex, INDEX16 HighIndex) DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetIndices"); hRet = g_pD3DDevice->DrawIndexedPrimitive( - D3DPT_LINELIST, - g_XboxBaseVertexIndex, - LowIndex, // minIndex - (HighIndex - LowIndex) + 1, // NumVertexIndices - 0, // startIndex - 1 // primCount + /*PrimitiveType=*/D3DPT_LINELIST, + /*BaseVertexIndex=*/0, // Note : Callers must apply this to the LowIndex/HighIndex argument values + /*MinVertrexIndex=*/LowIndex, + /*NumVertices=*/VERTICES_PER_LINE, + /*startIndex=*/0, + /*primCount=*/1 ); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitive(CxbxDrawIndexedClosingLine)"); @@ -6659,21 +6676,26 @@ void CxbxDrawIndexed(CxbxDrawContext &DrawContext) assert(DrawContext.pIndexData != nullptr); assert(IsValidCurrentShader()); - bool bQuadListAsTriangleList = (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_QUADLIST); - CxbxUpdateActiveIndexBuffer(DrawContext.pIndexData, DrawContext.dwVertexCount, bQuadListAsTriangleList); + bool bConvertQuadListToTriangleList = (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_QUADLIST); + ConvertedIndexBuffer& IB = CxbxUpdateActiveIndexBuffer(DrawContext.pIndexData, DrawContext.dwVertexCount, bConvertQuadListToTriangleList); VertexBufferConverter.Apply(&DrawContext); - //Walk through index buffer - // Determine highest and lowest index in use : - INDEX16 LowIndex, HighIndex; - WalkIndexBuffer(LowIndex, HighIndex, &(DrawContext.pIndexData[/*DrawContext.dwStartVertex=*/0]), DrawContext.dwVertexCount); + INT BaseVertexIndex = DrawContext.dwIndexBase; + UINT primCount = DrawContext.dwHostPrimitiveCount; + if (bConvertQuadListToTriangleList) { + LOG_TEST_CASE("X_D3DPT_QUADLIST"); + BaseVertexIndex = QuadToTriangleVertexCount(BaseVertexIndex); + primCount *= TRIANGLES_PER_QUAD; + } - UINT primCount = (bQuadListAsTriangleList ? TRIANGLES_PER_QUAD : 1) * DrawContext.dwHostPrimitiveCount; + // See https://docs.microsoft.com/en-us/windows/win32/direct3d9/rendering-from-vertex-and-index-buffers + // for an explanation on the function of the BaseVertexIndex, MinVertexIndex, + // NumVertices, StartIndex and StartIndex arguments. HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitive( /* PrimitiveType = */EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), - /* BaseVertexIndex = */DrawContext.dwIndexBase, - /* MinVertexIndex = */LowIndex, - /* NumVertices = */(HighIndex - LowIndex) + 1,//using index vertex span here. // TODO : g_EmuD3DActiveStreamSizes[0], // Note : ATI drivers are especially picky about this - + BaseVertexIndex, + /* MinVertexIndex = */IB.LowIndex, + /* NumVertices = */(IB.HighIndex - IB.LowIndex) + 1,//using index vertex span here. // TODO : g_EmuD3DActiveStreamSizes[0], // Note : ATI drivers are especially picky about this - // NumVertices should be the span of covered vertices in the active vertex buffer (TODO : Is stream 0 correct?) /* startIndex = DrawContext.dwStartVertex = */0, primCount); @@ -6682,15 +6704,18 @@ void CxbxDrawIndexed(CxbxDrawContext &DrawContext) g_dwPrimPerFrame += primCount; if (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_LINELOOP) { // Close line-loops using a final single line, drawn from the end to the start vertex - LOG_TEST_CASE("X_D3DPT_LINELOOP"); + if (BaseVertexIndex == 0) { + LOG_TEST_CASE("X_D3DPT_LINELOOP"); + } else { + LOG_TEST_CASE("X_D3DPT_LINELOOP (BaseVertexIndex > 0)"); + // Note : This is to find test-cases for the BaseVertexIndex addition below: + } // Read the end and start index from the supplied index data - LowIndex = DrawContext.pIndexData[0]; - HighIndex = DrawContext.pIndexData[DrawContext.dwHostPrimitiveCount]; + INDEX16 LowIndex = BaseVertexIndex + DrawContext.pIndexData[0]; + INDEX16 HighIndex = BaseVertexIndex + DrawContext.pIndexData[DrawContext.dwHostPrimitiveCount]; // If needed, swap so highest index is higher than lowest (duh) if (HighIndex < LowIndex) { - HighIndex ^= LowIndex; - LowIndex ^= HighIndex; - HighIndex ^= LowIndex; + std::swap(HighIndex, LowIndex); } // Draw the closing line using a helper function (which will SetIndices) @@ -6991,6 +7016,8 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices) if (DrawContext.XboxPrimitiveType == X_D3DPT_LINELOOP) { // Close line-loops using a final single line, drawn from the end to the start vertex LOG_TEST_CASE("X_D3DPT_LINELOOP"); // TODO : Text-cases needed + + assert(DrawContext.dwIndexBase == 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); // Draw the closing line using a helper function (which will SetIndices) @@ -7146,26 +7173,31 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) DrawContext.pXboxVertexStreamZeroData = pVertexStreamZeroData; DrawContext.uiXboxVertexStreamZeroStride = VertexStreamZeroStride; // Don't set DrawContext.pIndexData = (INDEX16*)pIndexData; // Used by GetVerticesInBuffer + // TODO : Is g_XboxBaseVertexIndex ignored by this call? // DrawContext.dwIndexBase = 0; VertexBufferConverter.Apply(&DrawContext); + INDEX16* pXboxIndexData = (INDEX16*)pIndexData; + // Walk through the index buffer INDEX16 LowIndex, HighIndex; - WalkIndexBuffer(LowIndex, HighIndex, (INDEX16*)pIndexData, DrawContext.dwVertexCount); + WalkIndexBuffer(LowIndex, HighIndex, pXboxIndexData, DrawContext.dwVertexCount); INDEX16* pHostIndexData; - bool bQuadListAsTriangleList = DrawContext.XboxPrimitiveType == X_D3DPT_QUADLIST; - if (bQuadListAsTriangleList) { + UINT PrimitiveCount = DrawContext.dwHostPrimitiveCount; + + bool bConvertQuadListToTriangleList = (DrawContext.XboxPrimitiveType == X_D3DPT_QUADLIST); + if (bConvertQuadListToTriangleList) { LOG_TEST_CASE("X_D3DPT_QUADLIST"); // Test-case : Buffy: The Vampire Slayer, FastLoad XDK Sample - pHostIndexData = CxbxInitializeQuadListIndexBuffer((INDEX16 *)pIndexData, VertexCount); + pHostIndexData = CxbxInitializeQuadListIndexBuffer(pXboxIndexData, VertexCount); + PrimitiveCount *= TRIANGLES_PER_QUAD; // Note, that LowIndex and HighIndex won't change due to this quad-to-triangle conversion, // so it's less work to WalkIndexBuffer over the input instead of the converted index buffer. } else { // LOG_TEST_CASE("DrawIndexedPrimitiveUP"); // Test-case : Burnout, Namco Museum 50th Anniversary - pHostIndexData = (INDEX16 *)pIndexData; + pHostIndexData = pXboxIndexData; } - UINT PrimitiveCount = (bQuadListAsTriangleList ? TRIANGLES_PER_QUAD : 1) * DrawContext.dwHostPrimitiveCount; HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( /*PrimitiveType=*/EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), /*MinVertexIndex=*/LowIndex, @@ -7178,7 +7210,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) ); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitiveUP"); - if (bQuadListAsTriangleList) { + if (bConvertQuadListToTriangleList) { CxbxReleaseQuadListIndexBuffer(pHostIndexData); } @@ -7187,13 +7219,11 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) // Close line-loops using a final single line, drawn from the end to the start vertex LOG_TEST_CASE("X_D3DPT_LINELOOP"); // TODO : Which titles reach this case? // Read the end and start index from the supplied index data - LowIndex = ((INDEX16*)pIndexData)[0]; - HighIndex = ((INDEX16*)pIndexData)[DrawContext.dwHostPrimitiveCount]; + LowIndex = pXboxIndexData[0]; + HighIndex = pXboxIndexData[DrawContext.dwHostPrimitiveCount]; // If needed, swap so highest index is higher than lowest (duh) if (HighIndex < LowIndex) { - HighIndex ^= LowIndex; - LowIndex ^= HighIndex; - HighIndex ^= LowIndex; + std::swap(HighIndex, LowIndex); } // Close line-loops using a final single line, drawn from the end to the start vertex : diff --git a/src/core/hle/D3D8/XbConvert.h b/src/core/hle/D3D8/XbConvert.h index 1255d2fec..b02a4a46e 100644 --- a/src/core/hle/D3D8/XbConvert.h +++ b/src/core/hle/D3D8/XbConvert.h @@ -29,6 +29,8 @@ #include "core\hle\D3D8\XbD3D8Types.h" +#define VERTICES_PER_DOT 1 +#define VERTICES_PER_LINE 2 #define VERTICES_PER_TRIANGLE 3 #define VERTICES_PER_QUAD 4 #define TRIANGLES_PER_QUAD 2 From 2504de39c85380ba6c02d2e8b273d7435703b806 Mon Sep 17 00:00:00 2001 From: patrickvl Date: Tue, 22 Oct 2019 01:00:34 +0200 Subject: [PATCH 06/13] Turned ConvertedIndexBuffer into a class that in it's destructor releases the host index buffer. With that, added poor-mans IndexBuffer cache eviction. Moved our way of calling CreateIndexBuffer into it's own function. Condensed generation of quad-to-triangle index data into one function. Renamed a bunch of variables, and added a few comments. --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 314 ++++++++++------------ 1 file changed, 149 insertions(+), 165 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 697f04c94..a9f0ad029 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -163,8 +163,8 @@ static IDirect3DIndexBuffer *pClosingLineLoopIndexBuffer = nullptr; static IDirect3DIndexBuffer *pQuadToTriangleD3DIndexBuffer = nullptr; static UINT QuadToTriangleD3DIndexBuffer_Size = 0; // = NrOfQuadVertices -static INDEX16 *pQuadToTriangleIndexBuffer = nullptr; -static UINT QuadToTriangleIndexBuffer_Size = 0; // = NrOfQuadVertices +static INDEX16 *g_pQuadToTriangleIndexData = nullptr; +static UINT g_QuadToTriangleIndexData_Size = 0; // = NrOfQuadVertices static IDirect3DSurface *g_DefaultHostDepthBufferSuface = NULL; XTL::X_D3DSurface *g_XboxBackBufferSurface = NULL; @@ -2469,75 +2469,117 @@ constexpr UINT QuadToTriangleVertexCount(UINT NrOfQuadVertices) bool bUseClockWiseWindingOrder = true; // TODO : Should this be fetched from X_D3DRS_FRONTFACE (or X_D3DRS_CULLMODE)? // TODO : Move to own file -void CxbxConvertQuadListToTriangleListIndices( +constexpr void CxbxConvertQuadListToTriangleListIndices( INDEX16* pQuadIndexData, unsigned uNrOfTriangleIndices, INDEX16* pTriangleIndexData) { + assert(uNrOfTriangleIndices > 0); + assert(pTriangleIndexData); + unsigned i = 0; - while (i + (TRIANGLES_PER_QUAD * VERTICES_PER_TRIANGLE) < uNrOfTriangleIndices) { + unsigned j = 0; + while (i + (VERTICES_PER_TRIANGLE * TRIANGLES_PER_QUAD) <= uNrOfTriangleIndices) { if (bUseClockWiseWindingOrder) { // ABCD becomes ABC+CDA, so this is triangle 1 : - pTriangleIndexData[i + 0] = pQuadIndexData[0]; // A - pTriangleIndexData[i + 1] = pQuadIndexData[1]; // B - pTriangleIndexData[i + 2] = pQuadIndexData[2]; // C + pTriangleIndexData[i + 0] = pQuadIndexData ? pQuadIndexData[j + 0] : j + 0; // A + pTriangleIndexData[i + 1] = pQuadIndexData ? pQuadIndexData[j + 1] : j + 1; // B + pTriangleIndexData[i + 2] = pQuadIndexData ? pQuadIndexData[j + 2] : j + 2; // C i += VERTICES_PER_TRIANGLE; - // And this is triangle 2 : - pTriangleIndexData[i + 0] = pQuadIndexData[2]; // C - pTriangleIndexData[i + 1] = pQuadIndexData[3]; // D - pTriangleIndexData[i + 2] = pQuadIndexData[0]; // A + pTriangleIndexData[i + 0] = pQuadIndexData ? pQuadIndexData[j + 2] : j + 2; // C + pTriangleIndexData[i + 1] = pQuadIndexData ? pQuadIndexData[j + 3] : j + 3; // D + pTriangleIndexData[i + 2] = pQuadIndexData ? pQuadIndexData[j + 0] : j + 0; // A i += VERTICES_PER_TRIANGLE; - } - else { + } else { // ABCD becomes ADC+CBA, so this is triangle 1 : - pTriangleIndexData[i + 0] = pQuadIndexData[0]; // A - pTriangleIndexData[i + 1] = pQuadIndexData[3]; // D - pTriangleIndexData[i + 2] = pQuadIndexData[2]; // C + pTriangleIndexData[i + 0] = pQuadIndexData ? pQuadIndexData[j + 0] : j + 0; // A + pTriangleIndexData[i + 1] = pQuadIndexData ? pQuadIndexData[j + 3] : j + 3; // D + pTriangleIndexData[i + 2] = pQuadIndexData ? pQuadIndexData[j + 2] : j + 2; // C i += VERTICES_PER_TRIANGLE; - // And this is triangle 2 : - pTriangleIndexData[i + 0] = pQuadIndexData[2]; // C - pTriangleIndexData[i + 1] = pQuadIndexData[1]; // B - pTriangleIndexData[i + 2] = pQuadIndexData[0]; // A + pTriangleIndexData[i + 0] = pQuadIndexData ? pQuadIndexData[j + 2] : j + 2; // C + pTriangleIndexData[i + 1] = pQuadIndexData ? pQuadIndexData[j + 1] : j + 1; // B + pTriangleIndexData[i + 2] = pQuadIndexData ? pQuadIndexData[j + 0] : j + 0; // A i += VERTICES_PER_TRIANGLE; } // Next quad, please : - pQuadIndexData += VERTICES_PER_QUAD; + j += VERTICES_PER_QUAD; } } // TODO : Move to own file -INDEX16* CxbxInitializeQuadListIndexBuffer(INDEX16* pIndexData, unsigned QuadVertexCount) +INDEX16* CxbxCreateQuadListToTriangleListIndexData(INDEX16* pQuadIndexData, unsigned QuadVertexCount) { UINT NrOfTriangleVertices = QuadToTriangleVertexCount(QuadVertexCount); - INDEX16* pQuadToTriangleIndexBuffer = (INDEX16*)malloc(sizeof(INDEX16) * NrOfTriangleVertices); - CxbxConvertQuadListToTriangleListIndices(pIndexData, NrOfTriangleVertices, pQuadToTriangleIndexBuffer); + INDEX16* pQuadToTriangleIndexBuffer = (INDEX16*)malloc(NrOfTriangleVertices * sizeof(INDEX16)); + CxbxConvertQuadListToTriangleListIndices(pQuadIndexData, NrOfTriangleVertices, pQuadToTriangleIndexBuffer); return pQuadToTriangleIndexBuffer; } // TODO : Move to own file -void CxbxReleaseQuadListIndexBuffer(void* pHostIndexData) +void CxbxReleaseQuadListToTriangleListIndexData(void* pHostIndexData) { free(pHostIndexData); } -typedef struct { +class ConvertedIndexBuffer { +public: uint64_t Hash = 0; DWORD IndexCount = 0; IDirect3DIndexBuffer* pHostIndexBuffer = nullptr; - INDEX16 LowIndex; - INDEX16 HighIndex; -} ConvertedIndexBuffer; + INDEX16 LowIndex = 0; + INDEX16 HighIndex = 0; -std::unordered_map g_ConvertedIndexBuffers; + ~ConvertedIndexBuffer() + { + if (pHostIndexBuffer != nullptr) { + pHostIndexBuffer->Release(); + } + } +}; + +std::unordered_map g_IndexBufferCache; void CxbxRemoveIndexBuffer(PWORD pData) { // HACK: Never Free } +IDirect3DIndexBuffer* CxbxCreateIndexBuffer(unsigned IndexCount) +{ + LOG_INIT; // Allows use of DEBUG_D3DRESULT + + IDirect3DIndexBuffer* Result = nullptr; + + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb147168(v=vs.85).aspx + // "Managing Resources (Direct3D 9)" + // suggests "for resources which change with high frequency" [...] + // "D3DPOOL_DEFAULT along with D3DUSAGE_DYNAMIC should be used." + const D3DPOOL D3DPool = D3DPOOL_DEFAULT; // Was D3DPOOL_MANAGED + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb172625(v=vs.85).aspx + // "Buffers created with D3DPOOL_DEFAULT that do not specify D3DUSAGE_WRITEONLY may suffer a severe performance penalty." + const DWORD D3DUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY; // Was D3DUSAGE_WRITEONLY + + // Create a new native index buffer of the requested size : + UINT uiIndexBufferSize = IndexCount * sizeof(INDEX16); + HRESULT hRet = g_pD3DDevice->CreateIndexBuffer( + uiIndexBufferSize, // Size of the index buffer, in bytes. + D3DUsage, + /*Format=*/D3DFMT_INDEX16, + D3DPool, + &Result, + nullptr // pSharedHandle + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateIndexBuffer"); + if (FAILED(hRet)) { + assert(Result == nullptr); + } + + return Result; +} + ConvertedIndexBuffer& CxbxUpdateActiveIndexBuffer ( INDEX16* pIndexData, @@ -2558,40 +2600,27 @@ ConvertedIndexBuffer& CxbxUpdateActiveIndexBuffer LookupKey |= 1; } + // Poor-mans eviction policy : when exceeding treshold, clear entire cache : + if (g_IndexBufferCache.size() > 256) { + g_IndexBufferCache.clear(); // Note : ConvertedIndexBuffer destructor will release any assigned pHostIndexBuffer + } + // Create a reference to the active buffer - ConvertedIndexBuffer& indexBuffer = g_ConvertedIndexBuffers[LookupKey]; + ConvertedIndexBuffer& CacheEntry = g_IndexBufferCache[LookupKey]; // If the current index buffer size is too small, free it, so it'll get re-created - bool bNeedRepopulation = indexBuffer.IndexCount < RequiredIndexCount; + bool bNeedRepopulation = CacheEntry.IndexCount < RequiredIndexCount; if (bNeedRepopulation) { - if (indexBuffer.pHostIndexBuffer != nullptr) - indexBuffer.pHostIndexBuffer->Release(); + if (CacheEntry.pHostIndexBuffer != nullptr) + CacheEntry.pHostIndexBuffer->Release(); - indexBuffer = {}; + CacheEntry = {}; } // If we need to create an index buffer, do so. - if (indexBuffer.pHostIndexBuffer == nullptr) { - // https://msdn.microsoft.com/en-us/library/windows/desktop/bb147168(v=vs.85).aspx - // "Managing Resources (Direct3D 9)" - // suggests "for resources which change with high frequency" [...] - // "D3DPOOL_DEFAULT along with D3DUSAGE_DYNAMIC should be used." - const D3DPOOL D3DPool = D3DPOOL_DEFAULT; // Was D3DPOOL_MANAGED - // https://msdn.microsoft.com/en-us/library/windows/desktop/bb172625(v=vs.85).aspx - // "Buffers created with D3DPOOL_DEFAULT that do not specify D3DUSAGE_WRITEONLY may suffer a severe performance penalty." - const DWORD D3DUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY; // Was D3DUSAGE_WRITEONLY - // Create a new native index buffer of the above determined size : - UINT uiIndexBufferSize = sizeof(INDEX16) * RequiredIndexCount; - HRESULT hRet = g_pD3DDevice->CreateIndexBuffer( - uiIndexBufferSize, // Size of the index buffer, in bytes. - D3DUsage, - /*Format=*/D3DFMT_INDEX16, - D3DPool, - &indexBuffer.pHostIndexBuffer, - nullptr // pSharedHandle - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateIndexBuffer"); - if (FAILED(hRet)) + if (CacheEntry.pHostIndexBuffer == nullptr) { + CacheEntry.pHostIndexBuffer = CxbxCreateIndexBuffer(RequiredIndexCount); + if (!CacheEntry.pHostIndexBuffer) CxbxKrnlCleanup("CxbxUpdateActiveIndexBuffer: IndexBuffer Create Failed!"); } @@ -2600,43 +2629,44 @@ ConvertedIndexBuffer& CxbxUpdateActiveIndexBuffer uint64_t uiHash = ComputeHash(pIndexData, IndexCount * sizeof(INDEX16)); // If the data needs updating, do so - bNeedRepopulation |= (uiHash != indexBuffer.Hash); + bNeedRepopulation |= (uiHash != CacheEntry.Hash); if (bNeedRepopulation) { // Update the Index Count and the hash - indexBuffer.IndexCount = RequiredIndexCount; - indexBuffer.Hash = uiHash; + CacheEntry.IndexCount = RequiredIndexCount; + CacheEntry.Hash = uiHash; // Update the host index buffer - D3DLockData* pHostIndexBufferData = nullptr; - indexBuffer.pHostIndexBuffer->Lock(0, 0, &pHostIndexBufferData, D3DLOCK_DISCARD); + INDEX16* pHostIndexBufferData = nullptr; + HRESULT hRet = CacheEntry.pHostIndexBuffer->Lock(0, /*entire SizeToLock=*/0, (D3DLockData **)&pHostIndexBufferData, D3DLOCK_DISCARD); + DEBUG_D3DRESULT(hRet, "CacheEntry.pHostIndexBuffer->Lock"); if (pHostIndexBufferData == nullptr) { CxbxKrnlCleanup("CxbxUpdateActiveIndexBuffer: Could not lock index buffer!"); } // Determine highest and lowest index in use : - WalkIndexBuffer(indexBuffer.LowIndex, indexBuffer.HighIndex, pIndexData, IndexCount); + WalkIndexBuffer(CacheEntry.LowIndex, CacheEntry.HighIndex, pIndexData, IndexCount); if (bConvertQuadListToTriangleList) { // Note, that LowIndex and HighIndex won't change due to any quad-to-triangle conversion, // so it's less work to WalkIndexBuffer over the input instead of the converted index buffer. EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Converting quads to %d triangle indices (D3DFMT_INDEX16)", RequiredIndexCount); - CxbxConvertQuadListToTriangleListIndices((INDEX16*)pIndexData, RequiredIndexCount, (INDEX16*)pHostIndexBufferData); + CxbxConvertQuadListToTriangleListIndices((INDEX16*)pIndexData, RequiredIndexCount, pHostIndexBufferData); } else { EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Copying %d indices (D3DFMT_INDEX16)", IndexCount); memcpy(pHostIndexBufferData, pIndexData, IndexCount * sizeof(INDEX16)); } - indexBuffer.pHostIndexBuffer->Unlock(); + CacheEntry.pHostIndexBuffer->Unlock(); } // Activate the new native index buffer : - HRESULT hRet = g_pD3DDevice->SetIndices(indexBuffer.pHostIndexBuffer); + HRESULT hRet = g_pD3DDevice->SetIndices(CacheEntry.pHostIndexBuffer); // Note : Under Direct3D 9, the BaseVertexIndex argument is moved towards DrawIndexedPrimitive DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetIndices"); if (FAILED(hRet)) CxbxKrnlCleanup("CxbxUpdateActiveIndexBuffer: SetIndices Failed!"); - return indexBuffer; + return CacheEntry; } void Direct3D_CreateDevice_Start @@ -6488,57 +6518,21 @@ constexpr unsigned int IndicesPerPage = PAGE_SIZE / sizeof(INDEX16); constexpr unsigned int InputQuadsPerPage = ((IndicesPerPage * VERTICES_PER_QUAD) / VERTICES_PER_TRIANGLE) / TRIANGLES_PER_QUAD; // TODO : Move to own file -INDEX16 *CxbxAssureQuadListIndexBuffer(UINT NrOfQuadVertices) +INDEX16 *CxbxAssureQuadListIndexData(UINT NrOfQuadVertices) { - if (QuadToTriangleIndexBuffer_Size < NrOfQuadVertices) + if (g_QuadToTriangleIndexData_Size < NrOfQuadVertices) { - QuadToTriangleIndexBuffer_Size = RoundUp(NrOfQuadVertices, InputQuadsPerPage); - - UINT NrOfTriangleVertices = QuadToTriangleVertexCount(QuadToTriangleIndexBuffer_Size); - - if (pQuadToTriangleIndexBuffer != nullptr) - free(pQuadToTriangleIndexBuffer); - - pQuadToTriangleIndexBuffer = (INDEX16 *)malloc(sizeof(INDEX16) * NrOfTriangleVertices); - - UINT i = 0; - INDEX16 j = 0; - while (i + (TRIANGLES_PER_QUAD * VERTICES_PER_TRIANGLE) < NrOfTriangleVertices) - { - if (bUseClockWiseWindingOrder) { - // ABCD becomes ABC+CDA, so this is triangle 1 : - pQuadToTriangleIndexBuffer[i + 0] = j + 0; // A - pQuadToTriangleIndexBuffer[i + 1] = j + 1; // B - pQuadToTriangleIndexBuffer[i + 2] = j + 2; // C - i += VERTICES_PER_TRIANGLE; - - // And this is triangle 2 : - pQuadToTriangleIndexBuffer[i + 0] = j + 2; // C - pQuadToTriangleIndexBuffer[i + 1] = j + 3; // D - pQuadToTriangleIndexBuffer[i + 2] = j + 0; // A - i += VERTICES_PER_TRIANGLE; - } - else - { - // ABCD becomes ADC+CBA, so this is triangle 1 : - pQuadToTriangleIndexBuffer[i + 0] = j + 0; // A - pQuadToTriangleIndexBuffer[i + 1] = j + 3; // D - pQuadToTriangleIndexBuffer[i + 2] = j + 2; // C - i += VERTICES_PER_TRIANGLE; - - // And this is triangle 2 : - pQuadToTriangleIndexBuffer[i + 0] = j + 2; // C - pQuadToTriangleIndexBuffer[i + 1] = j + 1; // B - pQuadToTriangleIndexBuffer[i + 2] = j + 0; // A - i += VERTICES_PER_TRIANGLE; - } - - // Next quad, please : - j += VERTICES_PER_QUAD; + g_QuadToTriangleIndexData_Size = RoundUp(NrOfQuadVertices, InputQuadsPerPage); + UINT NrOfTriangleVertices = QuadToTriangleVertexCount(g_QuadToTriangleIndexData_Size); + if (g_pQuadToTriangleIndexData != nullptr) { + free(g_pQuadToTriangleIndexData); } + + g_pQuadToTriangleIndexData = (INDEX16 *)malloc(NrOfTriangleVertices * sizeof(INDEX16)); + CxbxConvertQuadListToTriangleListIndices(nullptr, NrOfTriangleVertices, g_pQuadToTriangleIndexData); } - return pQuadToTriangleIndexBuffer; + return g_pQuadToTriangleIndexData; } // TODO : Move to own file @@ -6553,7 +6547,6 @@ void CxbxAssureQuadListD3DIndexBuffer(UINT NrOfQuadVertices) // Round the number of indices up so we'll allocate whole pages QuadToTriangleD3DIndexBuffer_Size = RoundUp(NrOfQuadVertices, InputQuadsPerPage); UINT NrOfTriangleVertices = QuadToTriangleVertexCount(QuadToTriangleD3DIndexBuffer_Size); // 4 > 6 - UINT uiIndexBufferSize = sizeof(INDEX16) * NrOfTriangleVertices; // Create a new native index buffer of the above determined size : if (pQuadToTriangleD3DIndexBuffer != nullptr) { @@ -6561,28 +6554,19 @@ void CxbxAssureQuadListD3DIndexBuffer(UINT NrOfQuadVertices) pQuadToTriangleD3DIndexBuffer = nullptr; } - hRet = g_pD3DDevice->CreateIndexBuffer( - uiIndexBufferSize, - D3DUSAGE_WRITEONLY, - D3DFMT_INDEX16, - D3DPOOL_MANAGED, - &pQuadToTriangleD3DIndexBuffer, - nullptr // pSharedHandle - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateIndexBuffer"); - - if (FAILED(hRet)) + // Create a new native index buffer of the above determined size : + pQuadToTriangleD3DIndexBuffer = CxbxCreateIndexBuffer(NrOfTriangleVertices); + if (pQuadToTriangleD3DIndexBuffer == nullptr) CxbxKrnlCleanup("CxbxAssureQuadListD3DIndexBuffer : IndexBuffer Create Failed!"); // Put quadlist-to-triangle-list index mappings into this buffer : - INDEX16* pIndexBufferData = nullptr; - hRet = pQuadToTriangleD3DIndexBuffer->Lock(0, uiIndexBufferSize, (D3DLockData **)&pIndexBufferData, D3DLOCK_DISCARD); + INDEX16* pHostIndexBufferData = nullptr; + hRet = pQuadToTriangleD3DIndexBuffer->Lock(0, /*entire SizeToLock=*/0, (D3DLockData **)&pHostIndexBufferData, D3DLOCK_DISCARD); DEBUG_D3DRESULT(hRet, "pQuadToTriangleD3DIndexBuffer->Lock"); - - if (pIndexBufferData == nullptr) + if (pHostIndexBufferData == nullptr) CxbxKrnlCleanup("CxbxAssureQuadListD3DIndexBuffer : Could not lock index buffer!"); - memcpy(pIndexBufferData, CxbxAssureQuadListIndexBuffer(NrOfQuadVertices), uiIndexBufferSize); + CxbxConvertQuadListToTriangleListIndices(nullptr, NrOfTriangleVertices, pHostIndexBufferData); pQuadToTriangleD3DIndexBuffer->Unlock(); } @@ -6603,22 +6587,14 @@ void CxbxDrawIndexedClosingLine(INDEX16 LowIndex, INDEX16 HighIndex) HRESULT hRet; - const UINT uiIndexBufferSize = sizeof(INDEX16) * VERTICES_PER_LINE; // 4 bytes needed for 2 indices if (pClosingLineLoopIndexBuffer == nullptr) { - hRet = g_pD3DDevice->CreateIndexBuffer( - uiIndexBufferSize, - D3DUSAGE_WRITEONLY, - D3DFMT_INDEX16, - D3DPOOL_DEFAULT, - &pClosingLineLoopIndexBuffer, - nullptr // pSharedHandle - ); - if (FAILED(hRet)) + pClosingLineLoopIndexBuffer = CxbxCreateIndexBuffer(VERTICES_PER_LINE); + if (pClosingLineLoopIndexBuffer == nullptr) CxbxKrnlCleanup("Unable to create pClosingLineLoopIndexBuffer for D3DPT_LINELOOP emulation"); } INDEX16 *pCxbxClosingLineLoopIndexBufferData = nullptr; - hRet = pClosingLineLoopIndexBuffer->Lock(0, uiIndexBufferSize, (D3DLockData **)(&pCxbxClosingLineLoopIndexBufferData), D3DLOCK_DISCARD); + hRet = pClosingLineLoopIndexBuffer->Lock(0, /*entire SizeToLock=*/0, (D3DLockData **)&pCxbxClosingLineLoopIndexBufferData, D3DLOCK_DISCARD); DEBUG_D3DRESULT(hRet, "pClosingLineLoopIndexBuffer->Lock"); // Set the indices for the two VERTICES_PER_LINE : @@ -6633,7 +6609,7 @@ void CxbxDrawIndexedClosingLine(INDEX16 LowIndex, INDEX16 HighIndex) hRet = g_pD3DDevice->DrawIndexedPrimitive( /*PrimitiveType=*/D3DPT_LINELIST, - /*BaseVertexIndex=*/0, // Note : Callers must apply this to the LowIndex/HighIndex argument values + /*BaseVertexIndex=*/0, // Note : Callers must apply BaseVertexIndex to the LowIndex and HighIndex argument values /*MinVertrexIndex=*/LowIndex, /*NumVertices=*/VERTICES_PER_LINE, /*startIndex=*/0, @@ -6652,12 +6628,12 @@ void CxbxDrawIndexedClosingLineUP(INDEX16 LowIndex, INDEX16 HighIndex, void *pHo INDEX16 CxbxClosingLineIndices[2] = { LowIndex, HighIndex }; HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( - D3DPT_LINELIST, - LowIndex, // MinVertexIndex - (HighIndex - LowIndex) + 1, // NumVertexIndices, - 1, // PrimitiveCount, - CxbxClosingLineIndices, // pIndexData - D3DFMT_INDEX16, // IndexDataFormat + /*PrimitiveType=*/D3DPT_LINELIST, + /*MinVertexIndex=*/LowIndex, + /*NumVertices=*/(HighIndex - LowIndex) + 1, + /*PrimitiveCount=*/1, + /*pIndexData=*/CxbxClosingLineIndices, + /*IndexDataFormat=*/D3DFMT_INDEX16, pHostVertexStreamZeroData, uiHostVertexStreamZeroStride ); @@ -6674,16 +6650,18 @@ void CxbxDrawIndexed(CxbxDrawContext &DrawContext) assert(DrawContext.dwStartVertex == 0); assert(DrawContext.pIndexData != nullptr); + assert(DrawContext.dwVertexCount > 0); // TODO : If this fails, make responsible callers do an early-exit assert(IsValidCurrentShader()); bool bConvertQuadListToTriangleList = (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_QUADLIST); - ConvertedIndexBuffer& IB = CxbxUpdateActiveIndexBuffer(DrawContext.pIndexData, DrawContext.dwVertexCount, bConvertQuadListToTriangleList); - VertexBufferConverter.Apply(&DrawContext); + ConvertedIndexBuffer& CacheEntry = CxbxUpdateActiveIndexBuffer(DrawContext.pIndexData, DrawContext.dwVertexCount, bConvertQuadListToTriangleList); + VertexBufferConverter.Apply(&DrawContext); // Sets dwHostPrimitiveCount INT BaseVertexIndex = DrawContext.dwIndexBase; UINT primCount = DrawContext.dwHostPrimitiveCount; if (bConvertQuadListToTriangleList) { LOG_TEST_CASE("X_D3DPT_QUADLIST"); + // Convert draw arguments from quads to triangles : BaseVertexIndex = QuadToTriangleVertexCount(BaseVertexIndex); primCount *= TRIANGLES_PER_QUAD; } @@ -6694,8 +6672,8 @@ void CxbxDrawIndexed(CxbxDrawContext &DrawContext) HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitive( /* PrimitiveType = */EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), BaseVertexIndex, - /* MinVertexIndex = */IB.LowIndex, - /* NumVertices = */(IB.HighIndex - IB.LowIndex) + 1,//using index vertex span here. // TODO : g_EmuD3DActiveStreamSizes[0], // Note : ATI drivers are especially picky about this - + /* MinVertexIndex = */CacheEntry.LowIndex, + /* NumVertices = */(CacheEntry.HighIndex - CacheEntry.LowIndex) + 1,//using index vertex span here. // TODO : g_EmuD3DActiveStreamSizes[0], // Note : ATI drivers are especially picky about this - // NumVertices should be the span of covered vertices in the active vertex buffer (TODO : Is stream 0 correct?) /* startIndex = DrawContext.dwStartVertex = */0, primCount); @@ -6740,7 +6718,7 @@ void CxbxDrawPrimitiveUP(CxbxDrawContext &DrawContext) if (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_QUADLIST) { // LOG_TEST_CASE("X_D3DPT_QUADLIST"); // test-case : X-Marbles and XDK Sample PlayField // Draw quadlists using a single 'quad-to-triangle mapping' index buffer : - INDEX16 *pIndexData = CxbxAssureQuadListIndexBuffer(DrawContext.dwVertexCount); + INDEX16 *pIndexData = CxbxAssureQuadListIndexData(DrawContext.dwVertexCount); // Convert quad vertex-count to triangle vertex count : UINT PrimitiveCount = DrawContext.dwHostPrimitiveCount * TRIANGLES_PER_QUAD; @@ -6969,9 +6947,12 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices) VertexBufferConverter.Apply(&DrawContext); if (DrawContext.XboxPrimitiveType == X_D3DPT_QUADLIST) { - // LOG_TEST_CASE("X_D3DPT_QUADLIST"); // test-case : ?X-Marbles and XDK Sample (Cartoon, ?maybe PlayField?) - if (StartVertex > 0) { - LOG_TEST_CASE("X_D3DPT_QUADLIST StartVertex > 0"); + if (StartVertex == 0) { + //LOG_TEST_CASE("X_D3DPT_QUADLIST (StartVertex == 0)"); // disabled, hit too often + // test-case : ?X-Marbles + // test-case XDK Samples : AlphaFog, AntiAlias, BackBufferScale, BeginPush, Cartoon, TrueTypeFont (?maybe PlayField?) + } else { + LOG_TEST_CASE("X_D3DPT_QUADLIST (StartVertex > 0)"); // test-case : BLiNX: the time sweeper // test-case : Call of Duty: Finest Hour // test-case : Halo - Combat Evolved @@ -6988,14 +6969,14 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices) UINT startIndex = QuadToTriangleVertexCount(DrawContext.dwStartVertex); UINT primCount = DrawContext.dwHostPrimitiveCount * TRIANGLES_PER_QUAD; // Determine highest and lowest index in use : - INDEX16 LowIndex = pQuadToTriangleIndexBuffer[startIndex]; + INDEX16 LowIndex = startIndex; INDEX16 HighIndex = LowIndex + (INDEX16)DrawContext.dwVertexCount - 1; - // Emulate a quad by drawing each as a fan of 2 triangles + // Emulate drawing quads by drawing each quad with two indexed triangles : HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitive( - /*PrimitiveType=*/D3DPT_TRIANGLELIST, // Draw indexed triangles instead of quads - 0, // BaseVertexIndex - LowIndex, // minIndex - (HighIndex - LowIndex) + 1, // NumVertices, + /*PrimitiveType=*/D3DPT_TRIANGLELIST, + /*BaseVertexIndex=*/0, + /*MinVertexIndex=*/LowIndex, + /*NumVertices=*/(HighIndex - LowIndex) + 1, startIndex, primCount ); @@ -7188,8 +7169,11 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) bool bConvertQuadListToTriangleList = (DrawContext.XboxPrimitiveType == X_D3DPT_QUADLIST); if (bConvertQuadListToTriangleList) { - LOG_TEST_CASE("X_D3DPT_QUADLIST"); // Test-case : Buffy: The Vampire Slayer, FastLoad XDK Sample - pHostIndexData = CxbxInitializeQuadListIndexBuffer(pXboxIndexData, VertexCount); + LOG_TEST_CASE("X_D3DPT_QUADLIST"); + // Test-case : Buffy: The Vampire Slayer + // Test-case : XDK samples : FastLoad, BackBufferScale, DisplacementMap, Donuts3D, VolumeLight, PersistDisplay, PolynomialTextureMaps, SwapCallback, Tiling, VolumeFog, DebugKeyboard, Gamepad + // Convert draw arguments from quads to triangles : + pHostIndexData = CxbxCreateQuadListToTriangleListIndexData(pXboxIndexData, VertexCount); PrimitiveCount *= TRIANGLES_PER_QUAD; // Note, that LowIndex and HighIndex won't change due to this quad-to-triangle conversion, // so it's less work to WalkIndexBuffer over the input instead of the converted index buffer. @@ -7203,7 +7187,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) /*MinVertexIndex=*/LowIndex, /*NumVertexIndices=*/(HighIndex - LowIndex) + 1, //this shall be Vertex Spans DrawContext.dwVertexCount, PrimitiveCount, - (const void *)pHostIndexData, + pHostIndexData, /*IndexDataFormat=*/D3DFMT_INDEX16, DrawContext.pHostVertexStreamZeroData, DrawContext.uiHostVertexStreamZeroStride @@ -7211,7 +7195,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitiveUP"); if (bConvertQuadListToTriangleList) { - CxbxReleaseQuadListIndexBuffer(pHostIndexData); + CxbxReleaseQuadListToTriangleListIndexData(pHostIndexData); } g_dwPrimPerFrame += PrimitiveCount; From 29ab156d448ddbb3892c9a8ce2940a05f37c78c8 Mon Sep 17 00:00:00 2001 From: patrickvl Date: Wed, 23 Oct 2019 15:42:03 +0200 Subject: [PATCH 07/13] Fix GetVerticesInBuffer for indexed draws, this might actually fix missing geometry. Draw closing line (for a line loop) through vertex copies (to avoid a potential performance penalty when the old, index-based approach spanned over many indices). Renamed a few variables to make their contents and purpose more clear --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 130 +++++++++++++--------- src/core/hle/D3D8/XbPushBuffer.cpp | 2 +- src/core/hle/D3D8/XbVertexBuffer.cpp | 31 +++--- src/core/hle/D3D8/XbVertexBuffer.h | 4 +- 4 files changed, 96 insertions(+), 71 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index a9f0ad029..d16574c4b 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -158,10 +158,10 @@ static DWORD g_SwapLast = 0; static CxbxVertexBufferConverter VertexBufferConverter = {}; // cached Direct3D state variable(s) -static IDirect3DIndexBuffer *pClosingLineLoopIndexBuffer = nullptr; +static IDirect3DIndexBuffer *g_pClosingLineLoopHostIndexBuffer = nullptr; -static IDirect3DIndexBuffer *pQuadToTriangleD3DIndexBuffer = nullptr; -static UINT QuadToTriangleD3DIndexBuffer_Size = 0; // = NrOfQuadVertices +static IDirect3DIndexBuffer *g_pQuadToTriangleHostIndexBuffer = nullptr; +static UINT g_QuadToTriangleHostIndexBuffer_Size = 0; // = NrOfQuadVertices static INDEX16 *g_pQuadToTriangleIndexData = nullptr; static UINT g_QuadToTriangleIndexData_Size = 0; // = NrOfQuadVertices @@ -2582,19 +2582,19 @@ IDirect3DIndexBuffer* CxbxCreateIndexBuffer(unsigned IndexCount) ConvertedIndexBuffer& CxbxUpdateActiveIndexBuffer ( - INDEX16* pIndexData, - unsigned IndexCount, + INDEX16* pXboxIndexData, + unsigned XboxIndexCount, bool bConvertQuadListToTriangleList ) { LOG_INIT; // Allows use of DEBUG_D3DRESULT - uint32_t LookupKey = (uint32_t)pIndexData; - unsigned RequiredIndexCount = IndexCount; + uint32_t LookupKey = (uint32_t)pXboxIndexData; + unsigned RequiredIndexCount = XboxIndexCount; if (bConvertQuadListToTriangleList) { LOG_TEST_CASE("bConvertQuadListToTriangleList"); - RequiredIndexCount = QuadToTriangleVertexCount(IndexCount); + RequiredIndexCount = QuadToTriangleVertexCount(XboxIndexCount); // For now, indicate the quad-to-triangles case using the otherwise // (due to alignment) always-zero least significant bit : LookupKey |= 1; @@ -2626,7 +2626,7 @@ ConvertedIndexBuffer& CxbxUpdateActiveIndexBuffer // TODO : Speeds this up, perhaps by hashing less often, and/or by // doing two hashes : a small subset regularly, all data less frequently. - uint64_t uiHash = ComputeHash(pIndexData, IndexCount * sizeof(INDEX16)); + uint64_t uiHash = ComputeHash(pXboxIndexData, XboxIndexCount * sizeof(INDEX16)); // If the data needs updating, do so bNeedRepopulation |= (uiHash != CacheEntry.Hash); @@ -2644,15 +2644,15 @@ ConvertedIndexBuffer& CxbxUpdateActiveIndexBuffer } // Determine highest and lowest index in use : - WalkIndexBuffer(CacheEntry.LowIndex, CacheEntry.HighIndex, pIndexData, IndexCount); + WalkIndexBuffer(CacheEntry.LowIndex, CacheEntry.HighIndex, pXboxIndexData, XboxIndexCount); if (bConvertQuadListToTriangleList) { // Note, that LowIndex and HighIndex won't change due to any quad-to-triangle conversion, // so it's less work to WalkIndexBuffer over the input instead of the converted index buffer. EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Converting quads to %d triangle indices (D3DFMT_INDEX16)", RequiredIndexCount); - CxbxConvertQuadListToTriangleListIndices((INDEX16*)pIndexData, RequiredIndexCount, pHostIndexBufferData); + CxbxConvertQuadListToTriangleListIndices((INDEX16*)pXboxIndexData, RequiredIndexCount, pHostIndexBufferData); } else { - EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Copying %d indices (D3DFMT_INDEX16)", IndexCount); - memcpy(pHostIndexBufferData, pIndexData, IndexCount * sizeof(INDEX16)); + EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Copying %d indices (D3DFMT_INDEX16)", XboxIndexCount); + memcpy(pHostIndexBufferData, pXboxIndexData, XboxIndexCount * sizeof(INDEX16)); } CacheEntry.pHostIndexBuffer->Unlock(); @@ -6542,37 +6542,37 @@ void CxbxAssureQuadListD3DIndexBuffer(UINT NrOfQuadVertices) HRESULT hRet; - if (QuadToTriangleD3DIndexBuffer_Size < NrOfQuadVertices) + if (g_QuadToTriangleHostIndexBuffer_Size < NrOfQuadVertices) { // Round the number of indices up so we'll allocate whole pages - QuadToTriangleD3DIndexBuffer_Size = RoundUp(NrOfQuadVertices, InputQuadsPerPage); - UINT NrOfTriangleVertices = QuadToTriangleVertexCount(QuadToTriangleD3DIndexBuffer_Size); // 4 > 6 + g_QuadToTriangleHostIndexBuffer_Size = RoundUp(NrOfQuadVertices, InputQuadsPerPage); + UINT NrOfTriangleVertices = QuadToTriangleVertexCount(g_QuadToTriangleHostIndexBuffer_Size); // 4 > 6 // Create a new native index buffer of the above determined size : - if (pQuadToTriangleD3DIndexBuffer != nullptr) { - pQuadToTriangleD3DIndexBuffer->Release(); - pQuadToTriangleD3DIndexBuffer = nullptr; + if (g_pQuadToTriangleHostIndexBuffer != nullptr) { + g_pQuadToTriangleHostIndexBuffer->Release(); // test-case : XDK PointSprites + g_pQuadToTriangleHostIndexBuffer = nullptr; } // Create a new native index buffer of the above determined size : - pQuadToTriangleD3DIndexBuffer = CxbxCreateIndexBuffer(NrOfTriangleVertices); - if (pQuadToTriangleD3DIndexBuffer == nullptr) + g_pQuadToTriangleHostIndexBuffer = CxbxCreateIndexBuffer(NrOfTriangleVertices); + if (g_pQuadToTriangleHostIndexBuffer == nullptr) CxbxKrnlCleanup("CxbxAssureQuadListD3DIndexBuffer : IndexBuffer Create Failed!"); // Put quadlist-to-triangle-list index mappings into this buffer : INDEX16* pHostIndexBufferData = nullptr; - hRet = pQuadToTriangleD3DIndexBuffer->Lock(0, /*entire SizeToLock=*/0, (D3DLockData **)&pHostIndexBufferData, D3DLOCK_DISCARD); - DEBUG_D3DRESULT(hRet, "pQuadToTriangleD3DIndexBuffer->Lock"); + hRet = g_pQuadToTriangleHostIndexBuffer->Lock(0, /*entire SizeToLock=*/0, (D3DLockData **)&pHostIndexBufferData, D3DLOCK_DISCARD); + DEBUG_D3DRESULT(hRet, "g_pQuadToTriangleHostIndexBuffer->Lock"); if (pHostIndexBufferData == nullptr) CxbxKrnlCleanup("CxbxAssureQuadListD3DIndexBuffer : Could not lock index buffer!"); CxbxConvertQuadListToTriangleListIndices(nullptr, NrOfTriangleVertices, pHostIndexBufferData); - pQuadToTriangleD3DIndexBuffer->Unlock(); + g_pQuadToTriangleHostIndexBuffer->Unlock(); } // Activate the new native index buffer : - hRet = g_pD3DDevice->SetIndices(pQuadToTriangleD3DIndexBuffer); + hRet = g_pD3DDevice->SetIndices(g_pQuadToTriangleHostIndexBuffer); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetIndices"); if (FAILED(hRet)) @@ -6587,24 +6587,24 @@ void CxbxDrawIndexedClosingLine(INDEX16 LowIndex, INDEX16 HighIndex) HRESULT hRet; - if (pClosingLineLoopIndexBuffer == nullptr) { - pClosingLineLoopIndexBuffer = CxbxCreateIndexBuffer(VERTICES_PER_LINE); - if (pClosingLineLoopIndexBuffer == nullptr) - CxbxKrnlCleanup("Unable to create pClosingLineLoopIndexBuffer for D3DPT_LINELOOP emulation"); + if (g_pClosingLineLoopHostIndexBuffer == nullptr) { + g_pClosingLineLoopHostIndexBuffer = CxbxCreateIndexBuffer(VERTICES_PER_LINE); + if (g_pClosingLineLoopHostIndexBuffer == nullptr) + CxbxKrnlCleanup("Unable to create g_pClosingLineLoopHostIndexBuffer for D3DPT_LINELOOP emulation"); } INDEX16 *pCxbxClosingLineLoopIndexBufferData = nullptr; - hRet = pClosingLineLoopIndexBuffer->Lock(0, /*entire SizeToLock=*/0, (D3DLockData **)&pCxbxClosingLineLoopIndexBufferData, D3DLOCK_DISCARD); - DEBUG_D3DRESULT(hRet, "pClosingLineLoopIndexBuffer->Lock"); + hRet = g_pClosingLineLoopHostIndexBuffer->Lock(0, /*entire SizeToLock=*/0, (D3DLockData **)&pCxbxClosingLineLoopIndexBufferData, D3DLOCK_DISCARD); + DEBUG_D3DRESULT(hRet, "g_pClosingLineLoopHostIndexBuffer->Lock"); // Set the indices for the two VERTICES_PER_LINE : pCxbxClosingLineLoopIndexBufferData[0] = LowIndex; pCxbxClosingLineLoopIndexBufferData[1] = HighIndex; - hRet = pClosingLineLoopIndexBuffer->Unlock(); - DEBUG_D3DRESULT(hRet, "pClosingLineLoopIndexBuffer->Unlock"); + hRet = g_pClosingLineLoopHostIndexBuffer->Unlock(); + DEBUG_D3DRESULT(hRet, "g_pClosingLineLoopHostIndexBuffer->Unlock"); - hRet = g_pD3DDevice->SetIndices(pClosingLineLoopIndexBuffer); + hRet = g_pD3DDevice->SetIndices(g_pClosingLineLoopHostIndexBuffer); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetIndices"); hRet = g_pD3DDevice->DrawIndexedPrimitive( @@ -6625,6 +6625,10 @@ void CxbxDrawIndexedClosingLineUP(INDEX16 LowIndex, INDEX16 HighIndex, void *pHo { LOG_INIT // Allows use of DEBUG_D3DRESULT +#if 0 + // Since we can use pHostVertexStreamZeroData here, we can close the line simpler than + // via CxbxDrawIndexedClosingLine, by drawing two indices via DrawIndexedPrimitiveUP. + // (This is simpler because we use just indices and don't need to copy the vertices.) INDEX16 CxbxClosingLineIndices[2] = { LowIndex, HighIndex }; HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( @@ -6638,26 +6642,45 @@ void CxbxDrawIndexedClosingLineUP(INDEX16 LowIndex, INDEX16 HighIndex, void *pHo uiHostVertexStreamZeroStride ); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitiveUP(CxbxDrawIndexedClosingLineUP)"); +#else // TODO : If NumVertices is high, performance might suffer - drawing a copy of the two vertices could be faster + // Since we can use pHostVertexStreamZeroData here, we can close the line simpler than + // via CxbxDrawIndexedClosingLine, by drawing two vertices via DrawPrimitiveUP. + // (This is simpler because we just copy the vertices, and don't need a separate index buffer.) + uint8_t VertexData[512]; assert(512 >= 2 * uiHostVertexStreamZeroStride); + uint8_t *FirstVertex = (uint8_t *)pHostVertexStreamZeroData + (LowIndex * uiHostVertexStreamZeroStride); + uint8_t *SecondVertex = (uint8_t *)pHostVertexStreamZeroData + (HighIndex * uiHostVertexStreamZeroStride); + + memcpy(VertexData, FirstVertex, uiHostVertexStreamZeroStride); + memcpy(VertexData + uiHostVertexStreamZeroStride, SecondVertex, uiHostVertexStreamZeroStride); + + HRESULT hRet = g_pD3DDevice->DrawPrimitiveUP( + /*PrimitiveType=*/D3DPT_LINELIST, + /*PrimitiveCount=*/1, + /*pVertexStreamZeroData=*/VertexData, + uiHostVertexStreamZeroStride + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawPrimitiveUP(CxbxDrawIndexedClosingLineUP)"); +#endif g_dwPrimPerFrame++; } -// Requires assigned pIndexData +// Requires assigned pXboxIndexData // Called by D3DDevice_DrawIndexedVertices and EmuExecutePushBufferRaw (twice) void CxbxDrawIndexed(CxbxDrawContext &DrawContext) { LOG_INIT // Allows use of DEBUG_D3DRESULT assert(DrawContext.dwStartVertex == 0); - assert(DrawContext.pIndexData != nullptr); + assert(DrawContext.pXboxIndexData != nullptr); assert(DrawContext.dwVertexCount > 0); // TODO : If this fails, make responsible callers do an early-exit assert(IsValidCurrentShader()); bool bConvertQuadListToTriangleList = (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_QUADLIST); - ConvertedIndexBuffer& CacheEntry = CxbxUpdateActiveIndexBuffer(DrawContext.pIndexData, DrawContext.dwVertexCount, bConvertQuadListToTriangleList); + ConvertedIndexBuffer& CacheEntry = CxbxUpdateActiveIndexBuffer(DrawContext.pXboxIndexData, DrawContext.dwVertexCount, bConvertQuadListToTriangleList); VertexBufferConverter.Apply(&DrawContext); // Sets dwHostPrimitiveCount - INT BaseVertexIndex = DrawContext.dwIndexBase; + INT BaseVertexIndex = DrawContext.dwBaseVertexIndex; UINT primCount = DrawContext.dwHostPrimitiveCount; if (bConvertQuadListToTriangleList) { LOG_TEST_CASE("X_D3DPT_QUADLIST"); @@ -6689,8 +6712,8 @@ void CxbxDrawIndexed(CxbxDrawContext &DrawContext) // Note : This is to find test-cases for the BaseVertexIndex addition below: } // Read the end and start index from the supplied index data - INDEX16 LowIndex = BaseVertexIndex + DrawContext.pIndexData[0]; - INDEX16 HighIndex = BaseVertexIndex + DrawContext.pIndexData[DrawContext.dwHostPrimitiveCount]; + INDEX16 LowIndex = BaseVertexIndex + DrawContext.pXboxIndexData[0]; + INDEX16 HighIndex = BaseVertexIndex + DrawContext.pXboxIndexData[DrawContext.dwHostPrimitiveCount]; // If needed, swap so highest index is higher than lowest (duh) if (HighIndex < LowIndex) { std::swap(HighIndex, LowIndex); @@ -6710,9 +6733,9 @@ void CxbxDrawPrimitiveUP(CxbxDrawContext &DrawContext) LOG_INIT // Allows use of DEBUG_D3DRESULT assert(DrawContext.dwStartVertex == 0); - assert(DrawContext.pXboxVertexStreamZeroData != NULL); + assert(DrawContext.pXboxVertexStreamZeroData != xbnullptr); assert(DrawContext.uiXboxVertexStreamZeroStride > 0); - assert(DrawContext.dwIndexBase == 0); // No IndexBase under Draw*UP + assert(DrawContext.dwBaseVertexIndex == 0); // No IndexBase under Draw*UP VertexBufferConverter.Apply(&DrawContext); if (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_QUADLIST) { @@ -6727,8 +6750,9 @@ void CxbxDrawPrimitiveUP(CxbxDrawContext &DrawContext) INDEX16 LowIndex = 0; INDEX16 HighIndex = (INDEX16)(DrawContext.dwVertexCount - 1); + // Draw indexed triangles instead of quads HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( - /*PrimitiveType=*/D3DPT_TRIANGLELIST, // Draw indexed triangles instead of quads + /*PrimitiveType=*/D3DPT_TRIANGLELIST, /*MinVertexIndex=*/LowIndex, /*NumVertexIndices=*/(HighIndex - LowIndex) + 1, PrimitiveCount, @@ -6754,9 +6778,6 @@ void CxbxDrawPrimitiveUP(CxbxDrawContext &DrawContext) g_dwPrimPerFrame += DrawContext.dwHostPrimitiveCount; if (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_LINELOOP) { // Note : XDK samples reaching this case : DebugKeyboard, Gamepad, Tiling, ShadowBuffer - // Since we can use pHostVertexStreamZeroData here, we can close the line simpler than - // via CxbxDrawIndexedClosingLine, by drawing two indices via DrawIndexedPrimitiveUP. - // (This is simpler because we use just indices and don't need to copy the vertices.) // Close line-loops using a final single line, drawn from the end to the start vertex : CxbxDrawIndexedClosingLineUP( (INDEX16)0, // LowIndex @@ -6998,7 +7019,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices) // Close line-loops using a final single line, drawn from the end to the start vertex LOG_TEST_CASE("X_D3DPT_LINELOOP"); // TODO : Text-cases needed - assert(DrawContext.dwIndexBase == 0); // if this fails, it needs to be added to LowIndex and HighIndex : + 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); // Draw the closing line using a helper function (which will SetIndices) @@ -7098,8 +7119,8 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVertices) DrawContext.XboxPrimitiveType = PrimitiveType; DrawContext.dwVertexCount = VertexCount; - DrawContext.dwIndexBase = g_XboxBaseVertexIndex; // Used by GetVerticesInBuffer - DrawContext.pIndexData = pIndexData; // Used by GetVerticesInBuffer + DrawContext.dwBaseVertexIndex = g_XboxBaseVertexIndex; // Used by GetVerticesInBuffer + DrawContext.pXboxIndexData = pIndexData; // Used by GetVerticesInBuffer // Test case JSRF draws all geometry through this function (only sparks are drawn via another method) // using X_D3DPT_TRIANGLELIST and X_D3DPT_TRIANGLESTRIP PrimitiveType @@ -7148,18 +7169,17 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) if (IsValidCurrentShader()) { CxbxDrawContext DrawContext = {}; + INDEX16* pXboxIndexData = (INDEX16*)pIndexData; DrawContext.XboxPrimitiveType = PrimitiveType; - DrawContext.dwVertexCount = VertexCount; + DrawContext.dwVertexCount = VertexCount; DrawContext.pXboxVertexStreamZeroData = pVertexStreamZeroData; DrawContext.uiXboxVertexStreamZeroStride = VertexStreamZeroStride; - // Don't set DrawContext.pIndexData = (INDEX16*)pIndexData; // Used by GetVerticesInBuffer - // TODO : Is g_XboxBaseVertexIndex ignored by this call? // DrawContext.dwIndexBase = 0; + DrawContext.pXboxIndexData = pXboxIndexData; // Used by GetVerticesInBuffer + // TODO : Is g_XboxBaseVertexIndex ignored by this call? // DrawContext.dwBaseVertexIndex = 0; VertexBufferConverter.Apply(&DrawContext); - INDEX16* pXboxIndexData = (INDEX16*)pIndexData; - // Walk through the index buffer INDEX16 LowIndex, HighIndex; WalkIndexBuffer(LowIndex, HighIndex, pXboxIndexData, DrawContext.dwVertexCount); @@ -7185,7 +7205,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( /*PrimitiveType=*/EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), /*MinVertexIndex=*/LowIndex, - /*NumVertexIndices=*/(HighIndex - LowIndex) + 1, //this shall be Vertex Spans DrawContext.dwVertexCount, + /*NumVertexIndices=*/(HighIndex - LowIndex) + 1, PrimitiveCount, pHostIndexData, /*IndexDataFormat=*/D3DFMT_INDEX16, diff --git a/src/core/hle/D3D8/XbPushBuffer.cpp b/src/core/hle/D3D8/XbPushBuffer.cpp index 8fa7f9ddf..3d75e7f52 100644 --- a/src/core/hle/D3D8/XbPushBuffer.cpp +++ b/src/core/hle/D3D8/XbPushBuffer.cpp @@ -259,7 +259,7 @@ void HLE_draw_inline_elements(NV2AState *d) DrawContext.XboxPrimitiveType = (XTL::X_D3DPRIMITIVETYPE)pg->primitive_mode; DrawContext.dwVertexCount = EmuD3DIndexCountToVertexCount(DrawContext.XboxPrimitiveType, uiIndexCount); - DrawContext.pIndexData = d->pgraph.inline_elements; // Used by GetVerticesInBuffer + DrawContext.pXboxIndexData = d->pgraph.inline_elements; // Used by GetVerticesInBuffer CxbxDrawIndexed(DrawContext); } diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index 3aa559b48..09886f102 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -115,26 +115,31 @@ CxbxVertexBufferConverter::CxbxVertexBufferConverter() m_pVertexShaderInfo = nullptr; } -size_t GetVerticesInBuffer(DWORD dwOffset, DWORD dwVertexCount, PWORD pIndexData, DWORD dwIndexBase) +size_t GetVerticesInBuffer(DWORD dwOffset, DWORD dwVertexCount, PWORD pIndexData, DWORD dwBaseVertexIndex) { // If we are drawing from an offset, we know that the vertex count must have offset vertices // before the first drawn vertices - dwVertexCount += dwOffset; - if (pIndexData == xbnullptr) { + dwVertexCount += dwOffset; + // When index data isn't given, there's nothing more to do + if (pIndexData == xbnullptr) { + // just return the vertex count we've arrived upon thus far return dwVertexCount; } // We are an indexed draw, so we have to parse the index buffer // The highest index we see can be used to determine the vertex buffer size - // TODO : Should we use WalkIndexBuffer() instead? - DWORD highestVertexIndex = 0; - for (DWORD i = 0; i < dwVertexCount; i++) { - if (highestVertexIndex < pIndexData[i]) { - highestVertexIndex = pIndexData[i]; + // TODO : Instead of this, use WalkIndexBuffer() output, which should be available for indexed draws... + unsigned HighIndex = 0; + for (unsigned i = dwOffset; i < dwVertexCount; i++) { + if (HighIndex < pIndexData[i]) { + HighIndex = pIndexData[i]; } - } - - return dwIndexBase + highestVertexIndex + 1; + } + + // Convert highest index (including the base offset) into a count + DWORD dwHighestVertexCount = dwBaseVertexIndex + HighIndex + 1; + // Return the biggest vertex count that can be reached + return std::max(dwVertexCount, dwHighestVertexCount); } int CountActiveD3DStreams() @@ -788,8 +793,8 @@ void CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext) pDrawContext->VerticesInBuffer = GetVerticesInBuffer( pDrawContext->dwStartVertex, pDrawContext->dwVertexCount, - pDrawContext->pIndexData, - pDrawContext->dwIndexBase + pDrawContext->pXboxIndexData, + pDrawContext->dwBaseVertexIndex ); // Get the number of streams diff --git a/src/core/hle/D3D8/XbVertexBuffer.h b/src/core/hle/D3D8/XbVertexBuffer.h index 126c23780..7ee6ed828 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.h +++ b/src/core/hle/D3D8/XbVertexBuffer.h @@ -36,8 +36,8 @@ typedef struct _CxbxDrawContext IN XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType; IN DWORD dwVertexCount; IN DWORD dwStartVertex; // Only D3DDevice_DrawVertices sets this (potentially higher than default 0) - IN PWORD pIndexData; - IN DWORD dwIndexBase; + IN PWORD pXboxIndexData; + IN DWORD dwBaseVertexIndex; IN size_t VerticesInBuffer; // Data if Draw...UP call IN PVOID pXboxVertexStreamZeroData; From eb9f610780f93718ea74d2a8d42ab26d943aaf1c Mon Sep 17 00:00:00 2001 From: patrickvl Date: Wed, 23 Oct 2019 16:36:10 +0200 Subject: [PATCH 08/13] Move LowIndex and HighIndex values into DrawContext, GetVerticesInBuffer doesn't need to determine HighIndex again. Because of this, GetVerticesInBuffer has been removed, and the determination was inlined into CxbxVertexBufferConverter::Apply() --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 26 +++++++----- src/core/hle/D3D8/XbPushBuffer.cpp | 2 +- src/core/hle/D3D8/XbVertexBuffer.cpp | 51 ++++++++--------------- src/core/hle/D3D8/XbVertexBuffer.h | 7 ++-- 4 files changed, 38 insertions(+), 48 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index d16574c4b..51307608d 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -6678,6 +6678,11 @@ void CxbxDrawIndexed(CxbxDrawContext &DrawContext) bool bConvertQuadListToTriangleList = (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_QUADLIST); ConvertedIndexBuffer& CacheEntry = CxbxUpdateActiveIndexBuffer(DrawContext.pXboxIndexData, DrawContext.dwVertexCount, bConvertQuadListToTriangleList); + + // Set LowIndex and HighIndex *before* VerticesInBuffer gets derived + DrawContext.LowIndex = CacheEntry.LowIndex; + DrawContext.HighIndex = CacheEntry.HighIndex; + VertexBufferConverter.Apply(&DrawContext); // Sets dwHostPrimitiveCount INT BaseVertexIndex = DrawContext.dwBaseVertexIndex; @@ -7119,8 +7124,8 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVertices) DrawContext.XboxPrimitiveType = PrimitiveType; DrawContext.dwVertexCount = VertexCount; - DrawContext.dwBaseVertexIndex = g_XboxBaseVertexIndex; // Used by GetVerticesInBuffer - DrawContext.pXboxIndexData = pIndexData; // Used by GetVerticesInBuffer + DrawContext.dwBaseVertexIndex = g_XboxBaseVertexIndex; // Used to derive VerticesInBuffer + DrawContext.pXboxIndexData = pIndexData; // Used to derive VerticesInBuffer // Test case JSRF draws all geometry through this function (only sparks are drawn via another method) // using X_D3DPT_TRIANGLELIST and X_D3DPT_TRIANGLESTRIP PrimitiveType @@ -7175,14 +7180,13 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) DrawContext.dwVertexCount = VertexCount; DrawContext.pXboxVertexStreamZeroData = pVertexStreamZeroData; DrawContext.uiXboxVertexStreamZeroStride = VertexStreamZeroStride; - DrawContext.pXboxIndexData = pXboxIndexData; // Used by GetVerticesInBuffer + DrawContext.pXboxIndexData = pXboxIndexData; // Used to derive VerticesInBuffer // TODO : Is g_XboxBaseVertexIndex ignored by this call? // DrawContext.dwBaseVertexIndex = 0; - VertexBufferConverter.Apply(&DrawContext); + // Determine LowIndex and HighIndex *before* VerticesInBuffer gets derived + WalkIndexBuffer(DrawContext.LowIndex, DrawContext.HighIndex, pXboxIndexData, VertexCount); - // Walk through the index buffer - INDEX16 LowIndex, HighIndex; - WalkIndexBuffer(LowIndex, HighIndex, pXboxIndexData, DrawContext.dwVertexCount); + VertexBufferConverter.Apply(&DrawContext); INDEX16* pHostIndexData; UINT PrimitiveCount = DrawContext.dwHostPrimitiveCount; @@ -7204,8 +7208,8 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( /*PrimitiveType=*/EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), - /*MinVertexIndex=*/LowIndex, - /*NumVertexIndices=*/(HighIndex - LowIndex) + 1, + /*MinVertexIndex=*/DrawContext.LowIndex, + /*NumVertexIndices=*/(DrawContext.HighIndex - DrawContext.LowIndex) + 1, PrimitiveCount, pHostIndexData, /*IndexDataFormat=*/D3DFMT_INDEX16, @@ -7223,8 +7227,8 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) // Close line-loops using a final single line, drawn from the end to the start vertex LOG_TEST_CASE("X_D3DPT_LINELOOP"); // TODO : Which titles reach this case? // Read the end and start index from the supplied index data - LowIndex = pXboxIndexData[0]; - HighIndex = pXboxIndexData[DrawContext.dwHostPrimitiveCount]; + INDEX16 LowIndex = pXboxIndexData[0]; + INDEX16 HighIndex = pXboxIndexData[DrawContext.dwHostPrimitiveCount]; // If needed, swap so highest index is higher than lowest (duh) if (HighIndex < LowIndex) { std::swap(HighIndex, LowIndex); diff --git a/src/core/hle/D3D8/XbPushBuffer.cpp b/src/core/hle/D3D8/XbPushBuffer.cpp index 3d75e7f52..aeddcaeb6 100644 --- a/src/core/hle/D3D8/XbPushBuffer.cpp +++ b/src/core/hle/D3D8/XbPushBuffer.cpp @@ -259,7 +259,7 @@ void HLE_draw_inline_elements(NV2AState *d) DrawContext.XboxPrimitiveType = (XTL::X_D3DPRIMITIVETYPE)pg->primitive_mode; DrawContext.dwVertexCount = EmuD3DIndexCountToVertexCount(DrawContext.XboxPrimitiveType, uiIndexCount); - DrawContext.pXboxIndexData = d->pgraph.inline_elements; // Used by GetVerticesInBuffer + DrawContext.pXboxIndexData = d->pgraph.inline_elements; CxbxDrawIndexed(DrawContext); } diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index 09886f102..a4ba45ec1 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -32,6 +32,7 @@ #include "common\util\hasher.h" #include "core\kernel\support\Emu.h" #include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_pD3DDevice +#include "core\hle\D3D8\Direct3D9\WalkIndexBuffer.h" // for WalkIndexBuffer #include "core\hle\D3D8\ResourceTracker.h" #include "core\hle\D3D8\XbPushBuffer.h" // for DxbxFVF_GetNumberOfTextureCoordinates #include "core\hle\D3D8\XbVertexBuffer.h" @@ -115,33 +116,6 @@ CxbxVertexBufferConverter::CxbxVertexBufferConverter() m_pVertexShaderInfo = nullptr; } -size_t GetVerticesInBuffer(DWORD dwOffset, DWORD dwVertexCount, PWORD pIndexData, DWORD dwBaseVertexIndex) -{ - // If we are drawing from an offset, we know that the vertex count must have offset vertices - // before the first drawn vertices - dwVertexCount += dwOffset; - // When index data isn't given, there's nothing more to do - if (pIndexData == xbnullptr) { - // just return the vertex count we've arrived upon thus far - return dwVertexCount; - } - - // We are an indexed draw, so we have to parse the index buffer - // The highest index we see can be used to determine the vertex buffer size - // TODO : Instead of this, use WalkIndexBuffer() output, which should be available for indexed draws... - unsigned HighIndex = 0; - for (unsigned i = dwOffset; i < dwVertexCount; i++) { - if (HighIndex < pIndexData[i]) { - HighIndex = pIndexData[i]; - } - } - - // Convert highest index (including the base offset) into a count - DWORD dwHighestVertexCount = dwBaseVertexIndex + HighIndex + 1; - // Return the biggest vertex count that can be reached - return std::max(dwVertexCount, dwHighestVertexCount); -} - int CountActiveD3DStreams() { int lastStreamIndex = 0; @@ -790,12 +764,23 @@ void CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext) m_pVertexShaderInfo = &(GetCxbxVertexShader(g_Xbox_VertexShader_Handle)->VertexShaderInfo); } - pDrawContext->VerticesInBuffer = GetVerticesInBuffer( - pDrawContext->dwStartVertex, - pDrawContext->dwVertexCount, - pDrawContext->pXboxIndexData, - pDrawContext->dwBaseVertexIndex - ); + // 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; + // Whhen this is an indexed draw, take the index buffer into account + if (pDrawContext->pXboxIndexData) { + if (pDrawContext->HighIndex == 0) { + // TODO : Instead of calling WalkIndexBuffer here, set LowIndex and HighIndex + // in all callers that end up here (since they might be able to avoid the call) + 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; + } // Get the number of streams m_uiNbrStreams = GetNbrStreams(pDrawContext); diff --git a/src/core/hle/D3D8/XbVertexBuffer.h b/src/core/hle/D3D8/XbVertexBuffer.h index 7ee6ed828..1d8ff9e9e 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.h +++ b/src/core/hle/D3D8/XbVertexBuffer.h @@ -36,9 +36,10 @@ typedef struct _CxbxDrawContext IN XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType; IN DWORD dwVertexCount; IN DWORD dwStartVertex; // Only D3DDevice_DrawVertices sets this (potentially higher than default 0) - IN PWORD pXboxIndexData; - IN DWORD dwBaseVertexIndex; - IN size_t VerticesInBuffer; + IN PWORD pXboxIndexData; + IN DWORD dwBaseVertexIndex; + IN INDEX16 LowIndex, HighIndex; // Set when pXboxIndexData is set + IN size_t VerticesInBuffer; // Set by CxbxVertexBufferConverter::Apply // Data if Draw...UP call IN PVOID pXboxVertexStreamZeroData; IN UINT uiXboxVertexStreamZeroStride; From ec2528355181bf4f6faecda552ed538f6d555d27 Mon Sep 17 00:00:00 2001 From: patrickvl Date: Thu, 24 Oct 2019 16:48:59 +0200 Subject: [PATCH 09/13] Addressed code review remarks, by adding explanations in comments and renaming a few arguments --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 96 +++++++++++++++-------- src/core/hle/D3D8/XbVertexBuffer.cpp | 3 +- 2 files changed, 65 insertions(+), 34 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 51307608d..e8138140b 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -161,10 +161,10 @@ static CxbxVertexBufferConverter VertexBufferConverter = {}; static IDirect3DIndexBuffer *g_pClosingLineLoopHostIndexBuffer = nullptr; static IDirect3DIndexBuffer *g_pQuadToTriangleHostIndexBuffer = nullptr; -static UINT g_QuadToTriangleHostIndexBuffer_Size = 0; // = NrOfQuadVertices +static UINT g_QuadToTriangleHostIndexBuffer_Size = 0; // = NrOfQuadIndices static INDEX16 *g_pQuadToTriangleIndexData = nullptr; -static UINT g_QuadToTriangleIndexData_Size = 0; // = NrOfQuadVertices +static UINT g_QuadToTriangleIndexData_Size = 0; // = NrOfQuadIndices static IDirect3DSurface *g_DefaultHostDepthBufferSuface = NULL; XTL::X_D3DSurface *g_XboxBackBufferSurface = NULL; @@ -2469,8 +2469,18 @@ constexpr UINT QuadToTriangleVertexCount(UINT NrOfQuadVertices) bool bUseClockWiseWindingOrder = true; // TODO : Should this be fetched from X_D3DRS_FRONTFACE (or X_D3DRS_CULLMODE)? // TODO : Move to own file +// This function convertes quad to triangle indices. +// When pXboxQuadIndexData is set, original quad indices are read from this buffer +// (this use-case is for when an indexed quad draw is to be emulated). +// When pXboxQuadIndexData is null, quad-emulating indices are generated +// (this use-case is for when a non-indexed quad draw is to be emulated). +// The number of indices to generate is specified through uNrOfTriangleIndices. +// Resulting triangle indices are written to pTriangleIndexData, which must +// be pre-allocated to fit the output data. +// (Note, this function is marked 'constexpr' to allow the compiler to optimize +// the case when pXboxQuadIndexData is null) constexpr void CxbxConvertQuadListToTriangleListIndices( - INDEX16* pQuadIndexData, + INDEX16* pXboxQuadIndexData, unsigned uNrOfTriangleIndices, INDEX16* pTriangleIndexData) { @@ -2482,25 +2492,25 @@ constexpr void CxbxConvertQuadListToTriangleListIndices( while (i + (VERTICES_PER_TRIANGLE * TRIANGLES_PER_QUAD) <= uNrOfTriangleIndices) { if (bUseClockWiseWindingOrder) { // ABCD becomes ABC+CDA, so this is triangle 1 : - pTriangleIndexData[i + 0] = pQuadIndexData ? pQuadIndexData[j + 0] : j + 0; // A - pTriangleIndexData[i + 1] = pQuadIndexData ? pQuadIndexData[j + 1] : j + 1; // B - pTriangleIndexData[i + 2] = pQuadIndexData ? pQuadIndexData[j + 2] : j + 2; // C + pTriangleIndexData[i + 0] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 0] : j + 0; // A + pTriangleIndexData[i + 1] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 1] : j + 1; // B + pTriangleIndexData[i + 2] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 2] : j + 2; // C i += VERTICES_PER_TRIANGLE; // And this is triangle 2 : - pTriangleIndexData[i + 0] = pQuadIndexData ? pQuadIndexData[j + 2] : j + 2; // C - pTriangleIndexData[i + 1] = pQuadIndexData ? pQuadIndexData[j + 3] : j + 3; // D - pTriangleIndexData[i + 2] = pQuadIndexData ? pQuadIndexData[j + 0] : j + 0; // A + pTriangleIndexData[i + 0] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 2] : j + 2; // C + pTriangleIndexData[i + 1] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 3] : j + 3; // D + pTriangleIndexData[i + 2] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 0] : j + 0; // A i += VERTICES_PER_TRIANGLE; } else { // ABCD becomes ADC+CBA, so this is triangle 1 : - pTriangleIndexData[i + 0] = pQuadIndexData ? pQuadIndexData[j + 0] : j + 0; // A - pTriangleIndexData[i + 1] = pQuadIndexData ? pQuadIndexData[j + 3] : j + 3; // D - pTriangleIndexData[i + 2] = pQuadIndexData ? pQuadIndexData[j + 2] : j + 2; // C + pTriangleIndexData[i + 0] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 0] : j + 0; // A + pTriangleIndexData[i + 1] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 3] : j + 3; // D + pTriangleIndexData[i + 2] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 2] : j + 2; // C i += VERTICES_PER_TRIANGLE; // And this is triangle 2 : - pTriangleIndexData[i + 0] = pQuadIndexData ? pQuadIndexData[j + 2] : j + 2; // C - pTriangleIndexData[i + 1] = pQuadIndexData ? pQuadIndexData[j + 1] : j + 1; // B - pTriangleIndexData[i + 2] = pQuadIndexData ? pQuadIndexData[j + 0] : j + 0; // A + pTriangleIndexData[i + 0] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 2] : j + 2; // C + pTriangleIndexData[i + 1] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 1] : j + 1; // B + pTriangleIndexData[i + 2] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 0] : j + 0; // A i += VERTICES_PER_TRIANGLE; } @@ -2510,11 +2520,20 @@ constexpr void CxbxConvertQuadListToTriangleListIndices( } // TODO : Move to own file -INDEX16* CxbxCreateQuadListToTriangleListIndexData(INDEX16* pQuadIndexData, unsigned QuadVertexCount) +// Called from EMUPATCH(D3DDevice_DrawIndexedVerticesUP) when PrimitiveType == X_D3DPT_QUADLIST. +// This API receives the number of vertices to draw (VertexCount), the index data that references +// vertices and a single stream of vertex data. The number of vertices to draw indicates the number +// of indices that are going to be fetched. The vertex data is referenced up to the highest index +// number present in the index data. +// To emulate drawing indexed quads, g_pD3DDevice->DrawIndexedPrimitiveUP is called on host, +// whereby the quad indices are converted to triangle indices. This implies for every four +// quad indices, we have to generate (two times three is) six triangle indices. (Note, that +// vertex data undergoes it's own Xbox-to-host conversion, independent from these indices.) +INDEX16* CxbxCreateQuadListToTriangleListIndexData(INDEX16* pXboxQuadIndexData, unsigned QuadVertexCount) { - UINT NrOfTriangleVertices = QuadToTriangleVertexCount(QuadVertexCount); - INDEX16* pQuadToTriangleIndexBuffer = (INDEX16*)malloc(NrOfTriangleVertices * sizeof(INDEX16)); - CxbxConvertQuadListToTriangleListIndices(pQuadIndexData, NrOfTriangleVertices, pQuadToTriangleIndexBuffer); + UINT NrOfTriangleIndices = QuadToTriangleVertexCount(QuadVertexCount); + INDEX16* pQuadToTriangleIndexBuffer = (INDEX16*)malloc(NrOfTriangleIndices * sizeof(INDEX16)); + CxbxConvertQuadListToTriangleListIndices(pXboxQuadIndexData, NrOfTriangleIndices, pQuadToTriangleIndexBuffer); return pQuadToTriangleIndexBuffer; } @@ -6518,35 +6537,46 @@ constexpr unsigned int IndicesPerPage = PAGE_SIZE / sizeof(INDEX16); constexpr unsigned int InputQuadsPerPage = ((IndicesPerPage * VERTICES_PER_QUAD) / VERTICES_PER_TRIANGLE) / TRIANGLES_PER_QUAD; // TODO : Move to own file -INDEX16 *CxbxAssureQuadListIndexData(UINT NrOfQuadVertices) +// Called by CxbxDrawPrimitiveUP (indirectly by D3DDevice_DrawVerticesUP, +// EmuExecutePushBufferRaw and EmuFlushIVB) when PrimitiveType == X_D3DPT_QUADLIST. +// Emulated by calling g_pD3DDevice->DrawIndexedPrimitiveUP with index data that maps +// quads to triangles. This function creates the index buffer that is needed for this; +// For every quad that must be drawn, we generate indices for two triangles. +// Note, that the resulting index data can be re-used for later comparable draw calls +// and only needs to grow when current length doesn't suffices for a larger draw. +INDEX16 *CxbxAssureQuadListIndexData(UINT NrOfQuadIndices) { - if (g_QuadToTriangleIndexData_Size < NrOfQuadVertices) + if (g_QuadToTriangleIndexData_Size < NrOfQuadIndices) { - g_QuadToTriangleIndexData_Size = RoundUp(NrOfQuadVertices, InputQuadsPerPage); - UINT NrOfTriangleVertices = QuadToTriangleVertexCount(g_QuadToTriangleIndexData_Size); + g_QuadToTriangleIndexData_Size = RoundUp(NrOfQuadIndices, InputQuadsPerPage); + UINT NrOfTriangleIndices = QuadToTriangleVertexCount(g_QuadToTriangleIndexData_Size); if (g_pQuadToTriangleIndexData != nullptr) { free(g_pQuadToTriangleIndexData); } - g_pQuadToTriangleIndexData = (INDEX16 *)malloc(NrOfTriangleVertices * sizeof(INDEX16)); - CxbxConvertQuadListToTriangleListIndices(nullptr, NrOfTriangleVertices, g_pQuadToTriangleIndexData); + g_pQuadToTriangleIndexData = (INDEX16 *)malloc(NrOfTriangleIndices * sizeof(INDEX16)); + CxbxConvertQuadListToTriangleListIndices(nullptr, NrOfTriangleIndices, g_pQuadToTriangleIndexData); } return g_pQuadToTriangleIndexData; } // TODO : Move to own file -void CxbxAssureQuadListD3DIndexBuffer(UINT NrOfQuadVertices) +// Makes a D3D IndexBuffer active that contains quadlist-to-trianglelist indices. +// Uses CxbxAssureQuadListIndexData to populate the index buffer with. +// Note, that the resulting index buffer can be re-used for later comparable draw calls +// and only needs to grow when current length doesn't sufficesw for a larger draw. +void CxbxAssureQuadListD3DIndexBuffer(UINT NrOfQuadIndices) { LOG_INIT // Allows use of DEBUG_D3DRESULT HRESULT hRet; - if (g_QuadToTriangleHostIndexBuffer_Size < NrOfQuadVertices) + if (g_QuadToTriangleHostIndexBuffer_Size < NrOfQuadIndices) { // Round the number of indices up so we'll allocate whole pages - g_QuadToTriangleHostIndexBuffer_Size = RoundUp(NrOfQuadVertices, InputQuadsPerPage); - UINT NrOfTriangleVertices = QuadToTriangleVertexCount(g_QuadToTriangleHostIndexBuffer_Size); // 4 > 6 + g_QuadToTriangleHostIndexBuffer_Size = RoundUp(NrOfQuadIndices, InputQuadsPerPage); + UINT NrOfTriangleIndices = QuadToTriangleVertexCount(g_QuadToTriangleHostIndexBuffer_Size); // 4 > 6 // Create a new native index buffer of the above determined size : if (g_pQuadToTriangleHostIndexBuffer != nullptr) { @@ -6555,7 +6585,7 @@ void CxbxAssureQuadListD3DIndexBuffer(UINT NrOfQuadVertices) } // Create a new native index buffer of the above determined size : - g_pQuadToTriangleHostIndexBuffer = CxbxCreateIndexBuffer(NrOfTriangleVertices); + g_pQuadToTriangleHostIndexBuffer = CxbxCreateIndexBuffer(NrOfTriangleIndices); if (g_pQuadToTriangleHostIndexBuffer == nullptr) CxbxKrnlCleanup("CxbxAssureQuadListD3DIndexBuffer : IndexBuffer Create Failed!"); @@ -6566,7 +6596,7 @@ void CxbxAssureQuadListD3DIndexBuffer(UINT NrOfQuadVertices) if (pHostIndexBufferData == nullptr) CxbxKrnlCleanup("CxbxAssureQuadListD3DIndexBuffer : Could not lock index buffer!"); - CxbxConvertQuadListToTriangleListIndices(nullptr, NrOfTriangleVertices, pHostIndexBufferData); + memcpy(pHostIndexBufferData, CxbxAssureQuadListIndexData(NrOfTriangleIndices), NrOfTriangleIndices * sizeof(INDEX16)); g_pQuadToTriangleHostIndexBuffer->Unlock(); } @@ -6610,7 +6640,7 @@ void CxbxDrawIndexedClosingLine(INDEX16 LowIndex, INDEX16 HighIndex) hRet = g_pD3DDevice->DrawIndexedPrimitive( /*PrimitiveType=*/D3DPT_LINELIST, /*BaseVertexIndex=*/0, // Note : Callers must apply BaseVertexIndex to the LowIndex and HighIndex argument values - /*MinVertrexIndex=*/LowIndex, + /*MinVertexIndex=*/LowIndex, /*NumVertices=*/VERTICES_PER_LINE, /*startIndex=*/0, /*primCount=*/1 @@ -6990,7 +7020,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices) // Draw quadlists using a single 'quad-to-triangle mapping' index buffer : // Assure & activate that special index buffer : - CxbxAssureQuadListD3DIndexBuffer(/*NrOfQuadVertices=*/DrawContext.dwVertexCount); + CxbxAssureQuadListD3DIndexBuffer(/*NrOfQuadIndices=*/DrawContext.dwVertexCount); // Convert quad vertex-count & start to triangle vertex count & start : UINT startIndex = QuadToTriangleVertexCount(DrawContext.dwStartVertex); UINT primCount = DrawContext.dwHostPrimitiveCount * TRIANGLES_PER_QUAD; diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index a4ba45ec1..153e978de 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -767,8 +767,9 @@ void CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext) // 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; - // Whhen this is an indexed draw, take the index buffer into account + // 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? if (pDrawContext->HighIndex == 0) { // TODO : Instead of calling WalkIndexBuffer here, set LowIndex and HighIndex // in all callers that end up here (since they might be able to avoid the call) From 4591e40712621c2e45ea124ee5752b0b40250614 Mon Sep 17 00:00:00 2001 From: patrickvl Date: Sat, 26 Oct 2019 17:36:45 +0200 Subject: [PATCH 10/13] Potential fix for Indiana Jones regression? --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 11 +++++++---- src/core/hle/D3D8/XbConvert.cpp | 24 +++++++++++------------ src/core/hle/D3D8/XbConvert.h | 12 +++++++----- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index e8138140b..ebff4f30c 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -174,7 +174,7 @@ static XTL::X_D3DSurface *g_pXboxDepthStencil = NULL; static DWORD g_dwVertexShaderUsage = 0; // TODO : Move to XbVertexShader.cpp static DWORD g_VertexShaderSlots[X_VSH_MAX_INSTRUCTION_COUNT]; -DWORD g_XboxBaseVertexIndex = 0; // a value that's effectively added to every VB Index stored in the index buffer +DWORD g_XboxBaseVertexIndex = 0; // Set by D3DDevice_SetIndices : a value that's effectively added to every VB Index stored in the index buffer DWORD g_DefaultPresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; DWORD g_PresentationIntervalOverride = 0; bool g_UnlockFramerateHack = false; // ignore the xbox presentation interval @@ -2668,7 +2668,7 @@ ConvertedIndexBuffer& CxbxUpdateActiveIndexBuffer // Note, that LowIndex and HighIndex won't change due to any quad-to-triangle conversion, // so it's less work to WalkIndexBuffer over the input instead of the converted index buffer. EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Converting quads to %d triangle indices (D3DFMT_INDEX16)", RequiredIndexCount); - CxbxConvertQuadListToTriangleListIndices((INDEX16*)pXboxIndexData, RequiredIndexCount, pHostIndexBufferData); + CxbxConvertQuadListToTriangleListIndices(pXboxIndexData, RequiredIndexCount, pHostIndexBufferData); } else { EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Copying %d indices (D3DFMT_INDEX16)", XboxIndexCount); memcpy(pHostIndexBufferData, pXboxIndexData, XboxIndexCount * sizeof(INDEX16)); @@ -6718,7 +6718,10 @@ void CxbxDrawIndexed(CxbxDrawContext &DrawContext) INT BaseVertexIndex = DrawContext.dwBaseVertexIndex; UINT primCount = DrawContext.dwHostPrimitiveCount; if (bConvertQuadListToTriangleList) { - LOG_TEST_CASE("X_D3DPT_QUADLIST"); + if (DrawContext.dwVertexCount == 4) + LOG_TEST_CASE("X_D3DPT_QUADLIST (single quad)"); // breakpoint location + else + LOG_TEST_CASE("X_D3DPT_QUADLIST"); // Convert draw arguments from quads to triangles : BaseVertexIndex = QuadToTriangleVertexCount(BaseVertexIndex); primCount *= TRIANGLES_PER_QUAD; @@ -7154,7 +7157,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVertices) DrawContext.XboxPrimitiveType = PrimitiveType; DrawContext.dwVertexCount = VertexCount; - DrawContext.dwBaseVertexIndex = g_XboxBaseVertexIndex; // Used to derive VerticesInBuffer + DrawContext.dwBaseVertexIndex = 0; // DO NOT set to g_XboxBaseVertexIndex (since pIndexData is given, we should ignore D3DDevice_SetIndices) DrawContext.pXboxIndexData = pIndexData; // Used to derive VerticesInBuffer // Test case JSRF draws all geometry through this function (only sparks are drawn via another method) diff --git a/src/core/hle/D3D8/XbConvert.cpp b/src/core/hle/D3D8/XbConvert.cpp index cde765777..6a477b735 100644 --- a/src/core/hle/D3D8/XbConvert.cpp +++ b/src/core/hle/D3D8/XbConvert.cpp @@ -1203,18 +1203,18 @@ UINT EmuD3DVertexToPrimitive[11][2] = // conversion table for xbox->pc primitive types D3DPRIMITIVETYPE EmuPrimitiveTypeLookup[] = { - /* NULL = 0 */ (D3DPRIMITIVETYPE)0, - /* D3DPT_POINTLIST = 1, */ D3DPT_POINTLIST, - /* D3DPT_LINELIST = 2, */ D3DPT_LINELIST, - /* D3DPT_LINELOOP = 3, Xbox */ D3DPT_LINESTRIP, - /* D3DPT_LINESTRIP = 4, */ D3DPT_LINESTRIP, - /* D3DPT_TRIANGLELIST = 5, */ D3DPT_TRIANGLELIST, - /* D3DPT_TRIANGLESTRIP = 6, */ D3DPT_TRIANGLESTRIP, - /* D3DPT_TRIANGLEFAN = 7, */ D3DPT_TRIANGLEFAN, - /* D3DPT_QUADLIST = 8, Xbox */ D3DPT_TRIANGLELIST, - /* D3DPT_QUADSTRIP = 9, Xbox */ D3DPT_TRIANGLESTRIP, - /* D3DPT_POLYGON = 10, Xbox */ D3DPT_TRIANGLEFAN, - /* D3DPT_MAX = 11, */ (D3DPRIMITIVETYPE)11 + /* NULL = 0 */ (D3DPRIMITIVETYPE)0, + /* X_D3DPT_POINTLIST = 1, */ D3DPT_POINTLIST, + /* X_D3DPT_LINELIST = 2, */ D3DPT_LINELIST, + /* X_D3DPT_LINELOOP = 3, Xbox */ D3DPT_LINESTRIP, + /* X_D3DPT_LINESTRIP = 4, */ D3DPT_LINESTRIP, + /* X_D3DPT_TRIANGLELIST = 5, */ D3DPT_TRIANGLELIST, + /* X_D3DPT_TRIANGLESTRIP = 6, */ D3DPT_TRIANGLESTRIP, + /* X_D3DPT_TRIANGLEFAN = 7, */ D3DPT_TRIANGLEFAN, + /* X_D3DPT_QUADLIST = 8, Xbox */ D3DPT_TRIANGLELIST, + /* X_D3DPT_QUADSTRIP = 9, Xbox */ D3DPT_TRIANGLESTRIP, + /* X_D3DPT_POLYGON = 10, Xbox */ D3DPT_TRIANGLEFAN, + /* X_D3DPT_MAX = 11, */ (D3DPRIMITIVETYPE)11 }; void EmuUnswizzleBox diff --git a/src/core/hle/D3D8/XbConvert.h b/src/core/hle/D3D8/XbConvert.h index b02a4a46e..7c7e13249 100644 --- a/src/core/hle/D3D8/XbConvert.h +++ b/src/core/hle/D3D8/XbConvert.h @@ -262,12 +262,14 @@ inline int EmuD3DPrimitive2VertexCount(XTL::X_D3DPRIMITIVETYPE PrimitiveType, in extern D3DPRIMITIVETYPE EmuPrimitiveTypeLookup[]; // convert xbox->pc primitive type -inline D3DPRIMITIVETYPE EmuXB2PC_D3DPrimitiveType(XTL::X_D3DPRIMITIVETYPE PrimitiveType) +inline D3DPRIMITIVETYPE EmuXB2PC_D3DPrimitiveType(XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType) { - if((DWORD)PrimitiveType == 0x7FFFFFFF) - return D3DPT_FORCE_DWORD; - - return EmuPrimitiveTypeLookup[PrimitiveType]; + if (XboxPrimitiveType >= XTL::X_D3DPT_MAX) { + LOG_TEST_CASE("XboxPrimitiveType too large"); + return D3DPT_FORCE_DWORD; + } + + return EmuPrimitiveTypeLookup[XboxPrimitiveType]; } inline int EmuD3DIndexCountToVertexCount(XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType, int IndexCount) From 91575822a84e2090a4381f171e68ff5b8d88b5a9 Mon Sep 17 00:00:00 2001 From: patrickvl Date: Sun, 27 Oct 2019 16:44:45 +0100 Subject: [PATCH 11/13] Corrected arguments for indexed quad draws --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 41 ++++++++++++----------- src/core/hle/D3D8/XbVertexBuffer.h | 2 +- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index ebff4f30c..b5105d806 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -6596,7 +6596,7 @@ void CxbxAssureQuadListD3DIndexBuffer(UINT NrOfQuadIndices) if (pHostIndexBufferData == nullptr) CxbxKrnlCleanup("CxbxAssureQuadListD3DIndexBuffer : Could not lock index buffer!"); - memcpy(pHostIndexBufferData, CxbxAssureQuadListIndexData(NrOfTriangleIndices), NrOfTriangleIndices * sizeof(INDEX16)); + memcpy(pHostIndexBufferData, CxbxAssureQuadListIndexData(NrOfQuadIndices), NrOfTriangleIndices * sizeof(INDEX16)); g_pQuadToTriangleHostIndexBuffer->Unlock(); } @@ -6722,14 +6722,14 @@ void CxbxDrawIndexed(CxbxDrawContext &DrawContext) LOG_TEST_CASE("X_D3DPT_QUADLIST (single quad)"); // breakpoint location else LOG_TEST_CASE("X_D3DPT_QUADLIST"); + // Convert draw arguments from quads to triangles : BaseVertexIndex = QuadToTriangleVertexCount(BaseVertexIndex); primCount *= TRIANGLES_PER_QUAD; } // See https://docs.microsoft.com/en-us/windows/win32/direct3d9/rendering-from-vertex-and-index-buffers - // for an explanation on the function of the BaseVertexIndex, MinVertexIndex, - // NumVertices, StartIndex and StartIndex arguments. + // 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, @@ -7016,27 +7016,30 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices) // test-case : Call of Duty: Finest Hour // test-case : Halo - Combat Evolved // test-case : Worms 3D Special Edition - // test-case : Tony Hawk's Pro Skater 2X - // test-case : XDK sample Lensflare - DrawContext.dwStartVertex = StartVertex; // Breakpoint location for testing. + // test-case : Tony Hawk's Pro Skater 2X (main menu entries) + // test-case : XDK sample Lensflare (4, for 10 flare-out quads that use a lineair texture; rendered incorrectly: https://youtu.be/idwlxHl9nAA?t=439) + DrawContext.dwStartVertex = StartVertex; // Breakpoint location for testing. } // Draw quadlists using a single 'quad-to-triangle mapping' index buffer : // Assure & activate that special index buffer : CxbxAssureQuadListD3DIndexBuffer(/*NrOfQuadIndices=*/DrawContext.dwVertexCount); - // Convert quad vertex-count & start to triangle vertex count & start : - UINT startIndex = QuadToTriangleVertexCount(DrawContext.dwStartVertex); + // 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 : UINT primCount = DrawContext.dwHostPrimitiveCount * TRIANGLES_PER_QUAD; - // Determine highest and lowest index in use : - INDEX16 LowIndex = startIndex; - INDEX16 HighIndex = LowIndex + (INDEX16)DrawContext.dwVertexCount - 1; + // See https://docs.microsoft.com/en-us/windows/win32/direct3d9/rendering-from-vertex-and-index-buffers + // for an explanation on the function of the BaseVertexIndex, MinVertexIndex, NumVertices and StartIndex arguments. // Emulate drawing quads by drawing each quad with two indexed triangles : HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitive( /*PrimitiveType=*/D3DPT_TRIANGLELIST, - /*BaseVertexIndex=*/0, - /*MinVertexIndex=*/LowIndex, - /*NumVertices=*/(HighIndex - LowIndex) + 1, - startIndex, + BaseVertexIndex, + /*MinVertexIndex=*/0, + NumVertices, + /*startIndex=*/0, primCount ); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitive(X_D3DPT_QUADLIST)"); @@ -7148,7 +7151,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVertices) return; } - // TODO : Call unpatched D3DDevice_SetStateVB(0); + // TODO : Call unpatched D3DDevice_SetStateVB(g_XboxBaseVertexIndex); CxbxUpdateNativeD3DResources(); @@ -7157,7 +7160,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVertices) DrawContext.XboxPrimitiveType = PrimitiveType; DrawContext.dwVertexCount = VertexCount; - DrawContext.dwBaseVertexIndex = 0; // DO NOT set to g_XboxBaseVertexIndex (since pIndexData is given, we should ignore D3DDevice_SetIndices) + DrawContext.dwBaseVertexIndex = g_XboxBaseVertexIndex; // Multiplied by vertex stride and added to the vertex buffer start DrawContext.pXboxIndexData = pIndexData; // Used to derive VerticesInBuffer // Test case JSRF draws all geometry through this function (only sparks are drawn via another method) @@ -7211,10 +7214,10 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) DrawContext.XboxPrimitiveType = PrimitiveType; DrawContext.dwVertexCount = VertexCount; + DrawContext.pXboxIndexData = pXboxIndexData; // Used to derive VerticesInBuffer + DrawContext.dwBaseVertexIndex = g_XboxBaseVertexIndex; // Multiplied by vertex stride and added to the vertex buffer start DrawContext.pXboxVertexStreamZeroData = pVertexStreamZeroData; DrawContext.uiXboxVertexStreamZeroStride = VertexStreamZeroStride; - DrawContext.pXboxIndexData = pXboxIndexData; // Used to derive VerticesInBuffer - // TODO : Is g_XboxBaseVertexIndex ignored by this call? // DrawContext.dwBaseVertexIndex = 0; // Determine LowIndex and HighIndex *before* VerticesInBuffer gets derived WalkIndexBuffer(DrawContext.LowIndex, DrawContext.HighIndex, pXboxIndexData, VertexCount); diff --git a/src/core/hle/D3D8/XbVertexBuffer.h b/src/core/hle/D3D8/XbVertexBuffer.h index 1d8ff9e9e..97d2a8605 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.h +++ b/src/core/hle/D3D8/XbVertexBuffer.h @@ -36,7 +36,7 @@ typedef struct _CxbxDrawContext IN XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType; IN DWORD dwVertexCount; IN DWORD dwStartVertex; // Only D3DDevice_DrawVertices sets this (potentially higher than default 0) - IN PWORD pXboxIndexData; + IN PWORD pXboxIndexData; // Set by D3DDevice_DrawIndexedVertices, D3DDevice_DrawIndexedVerticesUP and HLE_draw_inline_elements IN DWORD dwBaseVertexIndex; IN INDEX16 LowIndex, HighIndex; // Set when pXboxIndexData is set IN size_t VerticesInBuffer; // Set by CxbxVertexBufferConverter::Apply From fa76ffe140dfc88a42973012683ade5fbb73c4e7 Mon Sep 17 00:00:00 2001 From: patrickvl Date: Sun, 27 Oct 2019 17:47:55 +0100 Subject: [PATCH 12/13] Finally fixed Indiana Jones regressions (BaseVertexIndex mustn't be enlarged when drawing indexed quads through a converted index buffer). Also some renaming --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 21 ++++++++------ src/core/hle/D3D8/XbConvert.cpp | 28 +++++++++--------- src/core/hle/D3D8/XbConvert.h | 35 +++++++++-------------- src/core/hle/D3D8/XbPushBuffer.cpp | 2 +- src/core/hle/D3D8/XbVertexBuffer.cpp | 4 +-- src/core/hle/D3D8/XbVertexBuffer.h | 4 +-- 6 files changed, 45 insertions(+), 49 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index b5105d806..de20a193e 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -6708,6 +6708,7 @@ void CxbxDrawIndexed(CxbxDrawContext &DrawContext) bool bConvertQuadListToTriangleList = (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_QUADLIST); ConvertedIndexBuffer& CacheEntry = CxbxUpdateActiveIndexBuffer(DrawContext.pXboxIndexData, DrawContext.dwVertexCount, bConvertQuadListToTriangleList); + // Note : CxbxUpdateActiveIndexBuffer calls SetIndices // Set LowIndex and HighIndex *before* VerticesInBuffer gets derived DrawContext.LowIndex = CacheEntry.LowIndex; @@ -6723,8 +6724,11 @@ void CxbxDrawIndexed(CxbxDrawContext &DrawContext) else LOG_TEST_CASE("X_D3DPT_QUADLIST"); + if (BaseVertexIndex > 0) + LOG_TEST_CASE("X_D3DPT_QUADLIST (BaseVertexIndex > 0)"); + // Convert draw arguments from quads to triangles : - BaseVertexIndex = QuadToTriangleVertexCount(BaseVertexIndex); + // Note : BaseVertexIndex doesn't need mapping through QuadToTriangleVertexCount(BaseVertexIndex); primCount *= TRIANGLES_PER_QUAD; } @@ -6734,8 +6738,7 @@ void CxbxDrawIndexed(CxbxDrawContext &DrawContext) /* PrimitiveType = */EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), BaseVertexIndex, /* MinVertexIndex = */CacheEntry.LowIndex, - /* NumVertices = */(CacheEntry.HighIndex - CacheEntry.LowIndex) + 1,//using index vertex span here. // TODO : g_EmuD3DActiveStreamSizes[0], // Note : ATI drivers are especially picky about this - - // NumVertices should be the span of covered vertices in the active vertex buffer (TODO : Is stream 0 correct?) + /* NumVertices = */(CacheEntry.HighIndex - CacheEntry.LowIndex) + 1, /* startIndex = DrawContext.dwStartVertex = */0, primCount); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitive"); @@ -6989,7 +6992,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices) // Dxbx Note : In DrawVertices and DrawIndexedVertices, PrimitiveType may not be D3DPT_POLYGON - if (!EmuD3DValidVertexCount(PrimitiveType, VertexCount)) { + if (!IsValidXboxVertexCount(PrimitiveType, VertexCount)) { LOG_TEST_CASE("Invalid VertexCount"); return; } @@ -7017,7 +7020,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices) // test-case : Halo - Combat Evolved // test-case : Worms 3D Special Edition // test-case : Tony Hawk's Pro Skater 2X (main menu entries) - // test-case : XDK sample Lensflare (4, for 10 flare-out quads that use a lineair texture; rendered incorrectly: https://youtu.be/idwlxHl9nAA?t=439) + // test-case : XDK sample Lensflare (4, for 10 flare-out quads that use a linear texture; rendered incorrectly: https://youtu.be/idwlxHl9nAA?t=439) DrawContext.dwStartVertex = StartVertex; // Breakpoint location for testing. } @@ -7097,7 +7100,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVerticesUP) LOG_FUNC_ARG(VertexStreamZeroStride) LOG_FUNC_END; - if (!EmuD3DValidVertexCount(PrimitiveType, VertexCount)) { + if (!IsValidXboxVertexCount(PrimitiveType, VertexCount)) { LOG_TEST_CASE("Invalid VertexCount"); return; } @@ -7146,7 +7149,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVertices) LOG_FUNC_ARG(pIndexData) LOG_FUNC_END; - if (!EmuD3DValidVertexCount(PrimitiveType, VertexCount)) { + if (!IsValidXboxVertexCount(PrimitiveType, VertexCount)) { LOG_TEST_CASE("Invalid VertexCount"); return; } @@ -7199,7 +7202,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) LOG_FUNC_ARG(VertexStreamZeroStride) LOG_FUNC_END; - if (!EmuD3DValidVertexCount(PrimitiveType, VertexCount)) { + if (!IsValidXboxVertexCount(PrimitiveType, VertexCount)) { LOG_TEST_CASE("Invalid VertexCount"); return; } @@ -7215,7 +7218,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) DrawContext.XboxPrimitiveType = PrimitiveType; DrawContext.dwVertexCount = VertexCount; DrawContext.pXboxIndexData = pXboxIndexData; // Used to derive VerticesInBuffer - DrawContext.dwBaseVertexIndex = g_XboxBaseVertexIndex; // Multiplied by vertex stride and added to the vertex buffer start + // Note : D3DDevice_DrawIndexedVerticesUP does NOT use g_XboxBaseVertexIndex, so keep DrawContext.dwBaseVertexIndex at 0! DrawContext.pXboxVertexStreamZeroData = pVertexStreamZeroData; DrawContext.uiXboxVertexStreamZeroStride = VertexStreamZeroStride; diff --git a/src/core/hle/D3D8/XbConvert.cpp b/src/core/hle/D3D8/XbConvert.cpp index 6a477b735..46d72fabf 100644 --- a/src/core/hle/D3D8/XbConvert.cpp +++ b/src/core/hle/D3D8/XbConvert.cpp @@ -1181,27 +1181,27 @@ D3DMULTISAMPLE_TYPE EmuXB2PC_D3DMultiSampleFormat(DWORD Type) } // lookup table for converting vertex count to primitive count -UINT EmuD3DVertexToPrimitive[11][2] = +const unsigned g_XboxPrimitiveTypeInfo[11][2] = { - // First number the number of vertices per primitive - // Second number is the starting number of vertices the draw requires + // First number is the starting number of vertices the draw requires + // Second number the number of vertices per primitive // Example : Triangle list, has no starting vertices, and uses 3 vertices for each triangle // Example : Triangle strip, starts with 2 vertices, and adds 1 for each triangle {0, 0}, // NULL - {1, 0}, // X_D3DPT_POINTLIST - {2, 0}, // X_D3DPT_LINELIST - {1, 1}, // X_D3DPT_LINELOOP - {1, 1}, // X_D3DPT_LINESTRIP - {3, 0}, // X_D3DPT_TRIANGLELIST - {1, 2}, // X_D3DPT_TRIANGLESTRIP - {1, 2}, // X_D3DPT_TRIANGLEFAN - {4, 0}, // X_D3DPT_QUADLIST - {2, 2}, // X_D3DPT_QUADSTRIP - {1, 0}, // X_D3DPT_POLYGON + {0, 1}, // X_D3DPT_POINTLIST + {0, 2}, // X_D3DPT_LINELIST + {1, 1}, // X_D3DPT_LINELOOP + {1, 1}, // X_D3DPT_LINESTRIP + {0, 3}, // X_D3DPT_TRIANGLELIST + {2, 1}, // X_D3DPT_TRIANGLESTRIP + {2, 1}, // X_D3DPT_TRIANGLEFAN + {0, 4}, // X_D3DPT_QUADLIST + {2, 2}, // X_D3DPT_QUADSTRIP + {0, 1}, // X_D3DPT_POLYGON }; // conversion table for xbox->pc primitive types -D3DPRIMITIVETYPE EmuPrimitiveTypeLookup[] = +const D3DPRIMITIVETYPE g_XboxPrimitiveTypeToHost[] = { /* NULL = 0 */ (D3DPRIMITIVETYPE)0, /* X_D3DPT_POINTLIST = 1, */ D3DPT_POINTLIST, diff --git a/src/core/hle/D3D8/XbConvert.h b/src/core/hle/D3D8/XbConvert.h index 7c7e13249..8ee2c3cfe 100644 --- a/src/core/hle/D3D8/XbConvert.h +++ b/src/core/hle/D3D8/XbConvert.h @@ -233,33 +233,31 @@ inline D3DSTENCILOP EmuXB2PC_D3DSTENCILOP(XTL::X_D3DSTENCILOP Value) } // table used for vertex->primitive count conversion -extern UINT EmuD3DVertexToPrimitive[XTL::X_D3DPT_POLYGON + 1][2]; +extern const UINT g_XboxPrimitiveTypeInfo[XTL::X_D3DPT_POLYGON + 1][2]; + +inline bool IsValidXboxVertexCount(XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType, UINT VertexCount) +{ + assert(XboxPrimitiveType < XTL::X_D3DPT_MAX); -inline bool EmuD3DValidVertexCount(XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType, UINT VertexCount) -{ // Are there more vertices than required for setup? - if (VertexCount > EmuD3DVertexToPrimitive[XboxPrimitiveType][1]) + if (VertexCount > g_XboxPrimitiveTypeInfo[XboxPrimitiveType][0]) // Are the additional vertices exact multiples of the required additional vertices per primitive? - if (0 == ((VertexCount - EmuD3DVertexToPrimitive[XboxPrimitiveType][1]) % EmuD3DVertexToPrimitive[XboxPrimitiveType][0])) + if (0 == ((VertexCount - g_XboxPrimitiveTypeInfo[XboxPrimitiveType][0]) % g_XboxPrimitiveTypeInfo[XboxPrimitiveType][1])) return true; return false; } // convert from vertex count to primitive count (Xbox) -inline int EmuD3DVertex2PrimitiveCount(XTL::X_D3DPRIMITIVETYPE PrimitiveType, int VertexCount) +inline unsigned ConvertXboxVertexCountToPrimitiveCount(XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType, unsigned VertexCount) { - return (VertexCount - EmuD3DVertexToPrimitive[PrimitiveType][1]) / EmuD3DVertexToPrimitive[PrimitiveType][0]; + assert(XboxPrimitiveType < XTL::X_D3DPT_MAX); + + return (VertexCount - g_XboxPrimitiveTypeInfo[XboxPrimitiveType][0]) / g_XboxPrimitiveTypeInfo[XboxPrimitiveType][1]; } - -// convert from primitive count to vertex count (Xbox) -inline int EmuD3DPrimitive2VertexCount(XTL::X_D3DPRIMITIVETYPE PrimitiveType, int PrimitiveCount) -{ - return (PrimitiveCount * EmuD3DVertexToPrimitive[PrimitiveType][0]) + EmuD3DVertexToPrimitive[PrimitiveType][1]; -} - + // conversion table for xbox->pc primitive types -extern D3DPRIMITIVETYPE EmuPrimitiveTypeLookup[]; +extern const D3DPRIMITIVETYPE g_XboxPrimitiveTypeToHost[]; // convert xbox->pc primitive type inline D3DPRIMITIVETYPE EmuXB2PC_D3DPrimitiveType(XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType) @@ -269,12 +267,7 @@ inline D3DPRIMITIVETYPE EmuXB2PC_D3DPrimitiveType(XTL::X_D3DPRIMITIVETYPE XboxPr return D3DPT_FORCE_DWORD; } - return EmuPrimitiveTypeLookup[XboxPrimitiveType]; -} - -inline int EmuD3DIndexCountToVertexCount(XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType, int IndexCount) -{ - return IndexCount; + return g_XboxPrimitiveTypeToHost[XboxPrimitiveType]; } extern void EmuUnswizzleBox diff --git a/src/core/hle/D3D8/XbPushBuffer.cpp b/src/core/hle/D3D8/XbPushBuffer.cpp index aeddcaeb6..98e6d185a 100644 --- a/src/core/hle/D3D8/XbPushBuffer.cpp +++ b/src/core/hle/D3D8/XbPushBuffer.cpp @@ -258,7 +258,7 @@ void HLE_draw_inline_elements(NV2AState *d) CxbxDrawContext DrawContext = {}; DrawContext.XboxPrimitiveType = (XTL::X_D3DPRIMITIVETYPE)pg->primitive_mode; - DrawContext.dwVertexCount = EmuD3DIndexCountToVertexCount(DrawContext.XboxPrimitiveType, uiIndexCount); + DrawContext.dwVertexCount = uiIndexCount; DrawContext.pXboxIndexData = d->pgraph.inline_elements; CxbxDrawIndexed(DrawContext); diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index 153e978de..059a85842 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -805,9 +805,9 @@ void CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext) // handled by d3d : // Test-case : XDK Samples (FocusBlur, MotionBlur, Trees, PaintEffect, PlayField) // No need to set : pDrawContext->XboxPrimitiveType = X_D3DPT_TRIANGLESTRIP; - pDrawContext->dwHostPrimitiveCount = EmuD3DVertex2PrimitiveCount(XTL::X_D3DPT_TRIANGLESTRIP, pDrawContext->dwVertexCount); + pDrawContext->dwHostPrimitiveCount = ConvertXboxVertexCountToPrimitiveCount(XTL::X_D3DPT_TRIANGLESTRIP, pDrawContext->dwVertexCount); } else { - pDrawContext->dwHostPrimitiveCount = EmuD3DVertex2PrimitiveCount(pDrawContext->XboxPrimitiveType, pDrawContext->dwVertexCount); + pDrawContext->dwHostPrimitiveCount = ConvertXboxVertexCountToPrimitiveCount(pDrawContext->XboxPrimitiveType, pDrawContext->dwVertexCount); } if (pDrawContext->XboxPrimitiveType == XTL::X_D3DPT_POLYGON) { diff --git a/src/core/hle/D3D8/XbVertexBuffer.h b/src/core/hle/D3D8/XbVertexBuffer.h index 97d2a8605..2b5f5b9d9 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.h +++ b/src/core/hle/D3D8/XbVertexBuffer.h @@ -37,7 +37,7 @@ typedef struct _CxbxDrawContext IN DWORD dwVertexCount; IN DWORD dwStartVertex; // Only D3DDevice_DrawVertices sets this (potentially higher than default 0) IN PWORD pXboxIndexData; // Set by D3DDevice_DrawIndexedVertices, D3DDevice_DrawIndexedVerticesUP and HLE_draw_inline_elements - IN DWORD dwBaseVertexIndex; + IN DWORD dwBaseVertexIndex; // Set to g_XboxBaseVertexIndex in D3DDevice_DrawIndexedVertices IN INDEX16 LowIndex, HighIndex; // Set when pXboxIndexData is set IN size_t VerticesInBuffer; // Set by CxbxVertexBufferConverter::Apply // Data if Draw...UP call @@ -46,7 +46,7 @@ typedef struct _CxbxDrawContext // Values to be used on host OUT PVOID pHostVertexStreamZeroData; OUT UINT uiHostVertexStreamZeroStride; - OUT DWORD dwHostPrimitiveCount; + OUT DWORD dwHostPrimitiveCount; // Set by CxbxVertexBufferConverter::Apply } CxbxDrawContext; From 7508ec190e07a2ef33e8491edf4e749ee22aeeea Mon Sep 17 00:00:00 2001 From: PatrickvL Date: Mon, 28 Oct 2019 09:40:03 +0100 Subject: [PATCH 13/13] Comment-only change : Make "test-case" comment mentions consistent --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 6 +++--- src/core/hle/Intercept.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index de20a193e..12f142fd2 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -933,7 +933,7 @@ IDirect3DBaseTexture *GetHostBaseTexture(XTL::X_D3DResource *pXboxResource, DWOR return nullptr; if (GetXboxCommonResourceType(pXboxResource) != X_D3DCOMMON_TYPE_TEXTURE) { // Allows breakpoint below - // Burnout and Outrun 2006 hit this case (retrieving a surface instead of a texture) + // test-case : Burnout and Outrun 2006 hit this case (retrieving a surface instead of a texture) // TODO : Surfaces can be set in the texture stages, instead of textures // We'll need to wrap the surface somehow before using it as a texture LOG_TEST_CASE("GetHostBaseTexture called on a non-texture object"); @@ -6818,7 +6818,7 @@ void CxbxDrawPrimitiveUP(CxbxDrawContext &DrawContext) g_dwPrimPerFrame += DrawContext.dwHostPrimitiveCount; if (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_LINELOOP) { - // Note : XDK samples reaching this case : DebugKeyboard, Gamepad, Tiling, ShadowBuffer + // test-case : XDK samples reaching this case : DebugKeyboard, Gamepad, Tiling, ShadowBuffer // Close line-loops using a final single line, drawn from the end to the start vertex : CxbxDrawIndexedClosingLineUP( (INDEX16)0, // LowIndex @@ -7264,7 +7264,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) g_dwPrimPerFrame += PrimitiveCount; if (DrawContext.XboxPrimitiveType == X_D3DPT_LINELOOP) { // Close line-loops using a final single line, drawn from the end to the start vertex - LOG_TEST_CASE("X_D3DPT_LINELOOP"); // TODO : Which titles reach this case? + LOG_TEST_CASE("X_D3DPT_LINELOOP"); // TODO : Which titles reach this test-case? // Read the end and start index from the supplied index data INDEX16 LowIndex = pXboxIndexData[0]; INDEX16 HighIndex = pXboxIndexData[DrawContext.dwHostPrimitiveCount]; diff --git a/src/core/hle/Intercept.cpp b/src/core/hle/Intercept.cpp index 392ae0e7e..7a797298d 100644 --- a/src/core/hle/Intercept.cpp +++ b/src/core/hle/Intercept.cpp @@ -149,7 +149,7 @@ bool VerifySymbolAddressAgainstXRef(char *SymbolName, xbaddr Address, int XRef) CxbxPopupMessage(LOG_LEVEL::WARNING, CxbxMsgDlgIcon_Warn, "Verification of %s failed : XREF was 0x%.8X while lookup gave 0x%.8X", SymbolName, XRefAddr, Address); - // this case : Kabuki Warriors (for XREF_D3DTSS_TEXCOORDINDEX) + // test case : Kabuki Warriors (for XREF_D3DTSS_TEXCOORDINDEX) return false; }*/