From 86bb8cad97cc0a788101f974e0872c75face9a24 Mon Sep 17 00:00:00 2001 From: Luke Usher Date: Thu, 7 May 2020 20:39:34 +0100 Subject: [PATCH 1/9] Implement support for Xbox MSAA, reducing 'jaggies' Also fixes offset models in GTA3 and potentially others --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 110 +++++++++++++--------- src/core/hle/D3D8/XbVertexBuffer.cpp | 17 +++- 2 files changed, 78 insertions(+), 49 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index c05a48a8e..07e51267c 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -171,6 +171,7 @@ static XTL::X_D3DVBLANKCALLBACK g_pXbox_VerticalBlankCallback = xbnullptr; static XTL::X_D3DSurface *g_pXbox_DefaultDepthStencilSurface = xbnullptr; XTL::X_D3DSurface *g_pXbox_RenderTarget = xbnullptr; static XTL::X_D3DSurface *g_pXbox_DepthStencil = xbnullptr; + XTL::X_D3DMULTISAMPLE_TYPE g_XboxMultiSampleType = XTL::X_D3DMULTISAMPLE_NONE; XTL::X_VERTEXSHADERCONSTANTMODE g_Xbox_VertexShaderConstantMode = X_D3DSCM_192CONSTANTS; // Set by D3DDevice_SetShaderConstantMode, TODO : Move to XbVertexShader.cpp static XTL::DWORD g_Xbox_BaseVertexIndex = 0; // Set by D3DDevice_SetIndices, read by D3DDevice_DrawIndexedVertices : a value that's effectively added to every vertex index (as stored in an index buffer) by multiplying this by vertex stride and added to the vertex buffer start (see BaseVertexIndex in CxbxDrawIndexed) @@ -2842,10 +2843,7 @@ void Direct3D_CreateDevice_Start CxbxKrnlCleanup("Failed to init XboxTextureStates"); } - // Disable multisampling for now, this fixes an issue where GTA3 only renders to half-screen - // TODO: Find a better way of fixing this, we cannot just create larger backbuffers as it breaks - // many games, despite working in the dashboard - pPresentationParameters->MultiSampleType = XTL::X_D3DMULTISAMPLE_NONE; + g_XboxMultiSampleType = pPresentationParameters->MultiSampleType; // create default device *before* calling Xbox Direct3D_CreateDevice trampline // to avoid hitting EMUPATCH'es that need a valid g_pD3DDevice @@ -3085,11 +3083,9 @@ HRESULT WINAPI XTL::EMUPATCH(D3DDevice_Reset) // Unlike the host version of Reset, The Xbox version does not actually reset the entire device // Instead, it simply re-creates the backbuffer with a new configuration - // Just like CreateDevice, we need to unset the MultiSample flag for now - // This fixes the 'Render to half-screen' issue in some titles (Test Case: Max Payne, Gameplay) - // See Direct3D_CreateDevice_Start for more info - // We should leave the rest of the params as is, and let the Xbox code/surface cache take care of it for us - pPresentationParameters->MultiSampleType = XTL::X_D3DMULTISAMPLE_NONE; + + // Store the new multisampling configuration + g_XboxMultiSampleType = pPresentationParameters->MultiSampleType; // Since Reset will call create a new backbuffer surface, we can clear our current association // NOTE: We don't actually free the Xbox data, the Xbox side will do this for us when we call the trampoline below. @@ -3755,6 +3751,40 @@ float GetZScaleForSurface(XTL::X_D3DSurface* pSurface) return 1; } +void GetMultiSampleOffsetAndScale(float& xScale, float& yScale, float& xOffset, float& yOffset) +{ + // Default values + xScale = 1.0f; + yScale = 1.0f; + xOffset = 0.5f; + yOffset = 0.5f; + + if (g_XboxMultiSampleType != XTL::X_D3DMULTISAMPLE_NONE) { + switch (g_XboxMultiSampleType) + { + case XTL::X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_LINEAR: + case XTL::X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_QUINCUNX: + case XTL::X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_LINEAR: + case XTL::X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_GAUSSIAN: + case XTL::X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN: + xOffset = yOffset = 0.0f; + break; + } + + #define MULTISAMPLE_XSCALE_MASK 0x00f0 + #define MULTISAMPLE_XSCALE_SHIFT 4 + + #define MULTISAMPLE_YSCALE_MASK 0x000f + #define MULTISAMPLE_YSCALE_SHIFT 0 + + #define NULTISAMPLE_XSCALE(type) ((type & MULTISAMPLE_XSCALE_MASK) >> MULTISAMPLE_XSCALE_SHIFT) + #define NULTISAMPLE_YSCALE(type) ((type & MULTISAMPLE_YSCALE_MASK) >> MULTISAMPLE_YSCALE_SHIFT) + + xScale = (float)NULTISAMPLE_XSCALE(g_XboxMultiSampleType); + yScale = (float)NULTISAMPLE_YSCALE(g_XboxMultiSampleType); + } +} + void GetViewPortOffsetAndScale(float (&vOffset)[4], float(&vScale)[4]) { // Store viewport offset and scale in constant registers @@ -3762,6 +3792,14 @@ void GetViewPortOffsetAndScale(float (&vOffset)[4], float(&vScale)[4]) D3DVIEWPORT ViewPort; g_pD3DDevice->GetViewport(&ViewPort); + // NOTE: Due to how our GPU emulation works, we need to account for MSAA here, by adjusting the ViewPort dimensions + // This fixes the 'offset' models in GTA3 + float xScale, yScale; + float xOffset, yOffset; + GetMultiSampleOffsetAndScale(xScale, yScale, xOffset, yOffset); + ViewPort.Width /= xScale; + ViewPort.Height /= yScale; + // Calculate Width/Height scale & offset float scaleWidth = (2.0f / ViewPort.Width) * g_RenderScaleFactor; float scaleHeight = (2.0f / ViewPort.Height) * g_RenderScaleFactor; @@ -3777,40 +3815,10 @@ void GetViewPortOffsetAndScale(float (&vOffset)[4], float(&vScale)[4]) // TODO remove the code above as required // Default scale and offset. - // Multisample state will affect these - float xScale = 1.0f; - float yScale = 1.0f; - float xOffset = 0.5f; - float yOffset = 0.5f; - - // MULTISAMPLE options have offset of 0 - // Various sample sizes have various x and y scales - switch (g_EmuCDPD.XboxPresentationParameters.MultiSampleType) - { - case XTL::X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_LINEAR: - case XTL::X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_QUINCUNX: - case XTL::X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_LINEAR: - case XTL::X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_GAUSSIAN: - xOffset = yOffset = 0.0f; - break; - case XTL::X_D3DMULTISAMPLE_2_SAMPLES_SUPERSAMPLE_HORIZONTAL_LINEAR: - xScale = 2.0f; - break; - case XTL::X_D3DMULTISAMPLE_2_SAMPLES_SUPERSAMPLE_VERTICAL_LINEAR: - yScale = 2.0f; - break; - case XTL::X_D3DMULTISAMPLE_4_SAMPLES_SUPERSAMPLE_LINEAR: - case XTL::X_D3DMULTISAMPLE_4_SAMPLES_SUPERSAMPLE_GAUSSIAN: - xScale = yScale = 2.0f; - break; - case XTL::X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN: - xScale = yScale = 1.5f; - xOffset = yOffset = 0.0f; - break; - case XTL::X_D3DMULTISAMPLE_9_SAMPLES_SUPERSAMPLE_GAUSSIAN: - xScale = yScale = 3.0f; - break; - } + xScale = 1.0f; + yScale = 1.0f; + xOffset = 0.5f; + yOffset = 0.5f; // Xbox correct values? xOffset = xOffset + (1.0f / 32.0f); @@ -3918,6 +3926,13 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_SetViewport) } } + // Apply MSAA scale + float xScale, yScale; + float xOffset, yOffset; + GetMultiSampleOffsetAndScale(xScale, yScale, xOffset, yOffset); + HostViewPort.Width *= xScale; + HostViewPort.Height *= yScale; + HRESULT hRet = g_pD3DDevice->SetViewport(&HostViewPort); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetViewport"); @@ -5016,8 +5031,13 @@ DWORD WINAPI XTL::EMUPATCH(D3DDevice_Swap) DWORD XboxBackBufferWidth = GetPixelContainerWidth(g_pXbox_BackBufferSurface); DWORD XboxBackBufferHeight = GetPixelContainerHeight(g_pXbox_BackBufferSurface); - float xScale = (float)BackBufferDesc.Width / (float)XboxBackBufferWidth; - float yScale = (float)BackBufferDesc.Height / (float)XboxBackBufferHeight; + // We also need to account for any MSAA which may have enlarged the Xbox Backbuffer + float xScale, yScale; + float xOffset, yOffset; + GetMultiSampleOffsetAndScale(xScale, yScale, xOffset, yOffset); + + xScale = (float)BackBufferDesc.Width / ((float)XboxBackBufferWidth / xScale); + yScale = (float)BackBufferDesc.Height / ((float)XboxBackBufferHeight / yScale); EmuDestRect.top = (LONG)(EmuDestRect.top * yScale); EmuDestRect.left = (LONG)(EmuDestRect.left * xScale); diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index cc3d47332..de9cc936f 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -60,11 +60,13 @@ extern DWORD g_dwPrimPerFrame = 0; XTL::X_STREAMINPUT g_Xbox_SetStreamSource[X_VSH_MAX_STREAMS] = { 0 }; // Note : .Offset member is never set (so always 0) extern XTL::X_D3DSurface* g_pXbox_RenderTarget; -extern XTL::X_D3DSurface* g_pXbox_BackBufferSurface; +extern XTL::X_D3DSurface* g_pXbox_BackBufferSurface; +extern XTL::X_D3DMULTISAMPLE_TYPE g_XboxMultiSampleType; void *GetDataFromXboxResource(XTL::X_D3DResource *pXboxResource); bool GetHostRenderTargetDimensions(DWORD* pHostWidth, DWORD* pHostHeight, IDirect3DSurface* pHostRenderTarget = nullptr); uint32_t GetPixelContainerWidth(XTL::X_D3DPixelContainer* pPixelContainer); -uint32_t GetPixelContainerHeight(XTL::X_D3DPixelContainer* pPixelContainer); +uint32_t GetPixelContainerHeight(XTL::X_D3DPixelContainer* pPixelContainer); +void GetMultiSampleOffsetAndScale(float& xScale, float& yScale, float& xOffset, float& yOffset); void CxbxPatchedStream::Activate(CxbxDrawContext *pDrawContext, UINT uiStream) const { @@ -667,7 +669,7 @@ void CxbxVertexBufferConverter::ConvertStream HostRenderTarget_Height = XboxRenderTarget_Height; } - bool bNeedRHWTransform = XboxRenderTarget_Width < HostRenderTarget_Width && XboxRenderTarget_Height < HostRenderTarget_Height; + bool bNeedRHWTransform = g_XboxMultiSampleType != XTL::X_D3DMULTISAMPLE_NONE || (XboxRenderTarget_Width < HostRenderTarget_Width && XboxRenderTarget_Height < HostRenderTarget_Height); for (uint32_t uiVertex = 0; uiVertex < uiVertexCount; uiVertex++) { FLOAT *pVertexDataAsFloat = (FLOAT*)(&pHostVertexData[uiVertex * uiHostVertexStride]); @@ -678,7 +680,14 @@ void CxbxVertexBufferConverter::ConvertStream // Transforming always breaks render to non-upscaled textures: Only surfaces are upscaled, intentionally so if (bNeedRHWTransform) { pVertexDataAsFloat[0] *= g_RenderScaleFactor; - pVertexDataAsFloat[1] *= g_RenderScaleFactor; + pVertexDataAsFloat[1] *= g_RenderScaleFactor; + + // Apply MSAA scale + float xScale, yScale; + float xOffset, yOffset; + GetMultiSampleOffsetAndScale(xScale, yScale, xOffset, yOffset); + pVertexDataAsFloat[0] *= xScale; + pVertexDataAsFloat[1] *= yScale; } #if 0 From 543756d71256acbfc7084d1385d3e090f86cb6d8 Mon Sep 17 00:00:00 2001 From: Luke Usher Date: Fri, 8 May 2020 08:49:13 +0100 Subject: [PATCH 2/9] Amendments --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 18 +++++++++--------- src/core/hle/D3D8/XbVertexBuffer.cpp | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 07e51267c..77d577e59 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -171,7 +171,7 @@ static XTL::X_D3DVBLANKCALLBACK g_pXbox_VerticalBlankCallback = xbnullptr; static XTL::X_D3DSurface *g_pXbox_DefaultDepthStencilSurface = xbnullptr; XTL::X_D3DSurface *g_pXbox_RenderTarget = xbnullptr; static XTL::X_D3DSurface *g_pXbox_DepthStencil = xbnullptr; - XTL::X_D3DMULTISAMPLE_TYPE g_XboxMultiSampleType = XTL::X_D3DMULTISAMPLE_NONE; + XTL::X_D3DMULTISAMPLE_TYPE g_Xbox_MultiSampleType = XTL::X_D3DMULTISAMPLE_NONE; XTL::X_VERTEXSHADERCONSTANTMODE g_Xbox_VertexShaderConstantMode = X_D3DSCM_192CONSTANTS; // Set by D3DDevice_SetShaderConstantMode, TODO : Move to XbVertexShader.cpp static XTL::DWORD g_Xbox_BaseVertexIndex = 0; // Set by D3DDevice_SetIndices, read by D3DDevice_DrawIndexedVertices : a value that's effectively added to every vertex index (as stored in an index buffer) by multiplying this by vertex stride and added to the vertex buffer start (see BaseVertexIndex in CxbxDrawIndexed) @@ -2843,7 +2843,7 @@ void Direct3D_CreateDevice_Start CxbxKrnlCleanup("Failed to init XboxTextureStates"); } - g_XboxMultiSampleType = pPresentationParameters->MultiSampleType; + g_Xbox_MultiSampleType = pPresentationParameters->MultiSampleType; // create default device *before* calling Xbox Direct3D_CreateDevice trampline // to avoid hitting EMUPATCH'es that need a valid g_pD3DDevice @@ -3085,7 +3085,7 @@ HRESULT WINAPI XTL::EMUPATCH(D3DDevice_Reset) // Instead, it simply re-creates the backbuffer with a new configuration // Store the new multisampling configuration - g_XboxMultiSampleType = pPresentationParameters->MultiSampleType; + g_Xbox_MultiSampleType = pPresentationParameters->MultiSampleType; // Since Reset will call create a new backbuffer surface, we can clear our current association // NOTE: We don't actually free the Xbox data, the Xbox side will do this for us when we call the trampoline below. @@ -3759,8 +3759,8 @@ void GetMultiSampleOffsetAndScale(float& xScale, float& yScale, float& xOffset, xOffset = 0.5f; yOffset = 0.5f; - if (g_XboxMultiSampleType != XTL::X_D3DMULTISAMPLE_NONE) { - switch (g_XboxMultiSampleType) + if (g_Xbox_MultiSampleType != XTL::X_D3DMULTISAMPLE_NONE) { + switch (g_Xbox_MultiSampleType) { case XTL::X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_LINEAR: case XTL::X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_QUINCUNX: @@ -3777,11 +3777,11 @@ void GetMultiSampleOffsetAndScale(float& xScale, float& yScale, float& xOffset, #define MULTISAMPLE_YSCALE_MASK 0x000f #define MULTISAMPLE_YSCALE_SHIFT 0 - #define NULTISAMPLE_XSCALE(type) ((type & MULTISAMPLE_XSCALE_MASK) >> MULTISAMPLE_XSCALE_SHIFT) - #define NULTISAMPLE_YSCALE(type) ((type & MULTISAMPLE_YSCALE_MASK) >> MULTISAMPLE_YSCALE_SHIFT) + #define MULTISAMPLE_XSCALE(type) ((type & MULTISAMPLE_XSCALE_MASK) >> MULTISAMPLE_XSCALE_SHIFT) + #define MULTISAMPLE_YSCALE(type) ((type & MULTISAMPLE_YSCALE_MASK) >> MULTISAMPLE_YSCALE_SHIFT) - xScale = (float)NULTISAMPLE_XSCALE(g_XboxMultiSampleType); - yScale = (float)NULTISAMPLE_YSCALE(g_XboxMultiSampleType); + xScale = (float)MULTISAMPLE_XSCALE(g_Xbox_MultiSampleType); + yScale = (float)MULTISAMPLE_YSCALE(g_Xbox_MultiSampleType); } } diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index de9cc936f..496c1cba9 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -61,7 +61,7 @@ XTL::X_STREAMINPUT g_Xbox_SetStreamSource[X_VSH_MAX_STREAMS] = { 0 }; // Note : extern XTL::X_D3DSurface* g_pXbox_RenderTarget; extern XTL::X_D3DSurface* g_pXbox_BackBufferSurface; -extern XTL::X_D3DMULTISAMPLE_TYPE g_XboxMultiSampleType; +extern XTL::X_D3DMULTISAMPLE_TYPE g_Xbox_MultiSampleType; void *GetDataFromXboxResource(XTL::X_D3DResource *pXboxResource); bool GetHostRenderTargetDimensions(DWORD* pHostWidth, DWORD* pHostHeight, IDirect3DSurface* pHostRenderTarget = nullptr); uint32_t GetPixelContainerWidth(XTL::X_D3DPixelContainer* pPixelContainer); @@ -669,7 +669,7 @@ void CxbxVertexBufferConverter::ConvertStream HostRenderTarget_Height = XboxRenderTarget_Height; } - bool bNeedRHWTransform = g_XboxMultiSampleType != XTL::X_D3DMULTISAMPLE_NONE || (XboxRenderTarget_Width < HostRenderTarget_Width && XboxRenderTarget_Height < HostRenderTarget_Height); + bool bNeedRHWTransform = g_Xbox_MultiSampleType != XTL::X_D3DMULTISAMPLE_NONE || (XboxRenderTarget_Width < HostRenderTarget_Width && XboxRenderTarget_Height < HostRenderTarget_Height); for (uint32_t uiVertex = 0; uiVertex < uiVertexCount; uiVertex++) { FLOAT *pVertexDataAsFloat = (FLOAT*)(&pHostVertexData[uiVertex * uiHostVertexStride]); From b0bcc2fd9ba9382bebf4953138fa805bbb86c3f6 Mon Sep 17 00:00:00 2001 From: Luke Usher Date: Fri, 8 May 2020 09:04:58 +0100 Subject: [PATCH 3/9] Add LOG_TEST_CASE as per request --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 77d577e59..8ac092969 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -3766,8 +3766,11 @@ void GetMultiSampleOffsetAndScale(float& xScale, float& yScale, float& xOffset, case XTL::X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_QUINCUNX: case XTL::X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_LINEAR: case XTL::X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_GAUSSIAN: + xOffset = yOffset = 0.0f; + break; case XTL::X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN: xOffset = yOffset = 0.0f; + LOG_TEST_CASE("X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN"); break; } From e499d3ad07f308f3bd0e7c24345dd092c5485b41 Mon Sep 17 00:00:00 2001 From: Luke Usher Date: Fri, 8 May 2020 09:28:20 +0100 Subject: [PATCH 4/9] fix an issue where offset was not used --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 8ac092969..92314bfd5 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -3817,11 +3817,10 @@ void GetViewPortOffsetAndScale(float (&vOffset)[4], float(&vScale)[4]) // TODO will we need to do something here to support upscaling? // TODO remove the code above as required - // Default scale and offset. + // Reset to default scale (as we accounted for MSAA scale above) + // But don't reset the offset xScale = 1.0f; yScale = 1.0f; - xOffset = 0.5f; - yOffset = 0.5f; // Xbox correct values? xOffset = xOffset + (1.0f / 32.0f); From 23104c82801408044214ca9513a610c33c03daa2 Mon Sep 17 00:00:00 2001 From: Luke Usher Date: Fri, 8 May 2020 09:40:20 +0100 Subject: [PATCH 5/9] Fix an issue where rendering broke when MultiSampleType = 0 --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 92314bfd5..e3bb0b52f 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -3759,7 +3759,8 @@ void GetMultiSampleOffsetAndScale(float& xScale, float& yScale, float& xOffset, xOffset = 0.5f; yOffset = 0.5f; - if (g_Xbox_MultiSampleType != XTL::X_D3DMULTISAMPLE_NONE) { + // NOTE: Can be 0, so we need to check for that too + if (g_Xbox_MultiSampleType > 0 && g_Xbox_MultiSampleType != XTL::X_D3DMULTISAMPLE_NONE) { switch (g_Xbox_MultiSampleType) { case XTL::X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_LINEAR: From 85c0f608e24882fe4e85104a22ad9df450b577dc Mon Sep 17 00:00:00 2001 From: Luke Usher Date: Fri, 8 May 2020 09:52:54 +0100 Subject: [PATCH 6/9] Another minor fix --- src/core/hle/D3D8/XbVertexBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index 496c1cba9..bb4e8b05f 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -669,7 +669,7 @@ void CxbxVertexBufferConverter::ConvertStream HostRenderTarget_Height = XboxRenderTarget_Height; } - bool bNeedRHWTransform = g_Xbox_MultiSampleType != XTL::X_D3DMULTISAMPLE_NONE || (XboxRenderTarget_Width < HostRenderTarget_Width && XboxRenderTarget_Height < HostRenderTarget_Height); + bool bNeedRHWTransform = (g_Xbox_MultiSampleType > 0 && g_Xbox_MultiSampleType != XTL::X_D3DMULTISAMPLE_NONE) || (XboxRenderTarget_Width < HostRenderTarget_Width && XboxRenderTarget_Height < HostRenderTarget_Height); for (uint32_t uiVertex = 0; uiVertex < uiVertexCount; uiVertex++) { FLOAT *pVertexDataAsFloat = (FLOAT*)(&pHostVertexData[uiVertex * uiHostVertexStride]); From fec845f6f820996770983c47f2d7021b920546b9 Mon Sep 17 00:00:00 2001 From: Luke Usher Date: Fri, 8 May 2020 10:00:46 +0100 Subject: [PATCH 7/9] Cleaner fix to the above --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 2 +- src/core/hle/D3D8/XbVertexBuffer.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index e3bb0b52f..a132ccaa5 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -3760,7 +3760,7 @@ void GetMultiSampleOffsetAndScale(float& xScale, float& yScale, float& xOffset, yOffset = 0.5f; // NOTE: Can be 0, so we need to check for that too - if (g_Xbox_MultiSampleType > 0 && g_Xbox_MultiSampleType != XTL::X_D3DMULTISAMPLE_NONE) { + if (g_Xbox_MultiSampleType > 0) { switch (g_Xbox_MultiSampleType) { case XTL::X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_LINEAR: diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index bb4e8b05f..263a5db72 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -669,7 +669,7 @@ void CxbxVertexBufferConverter::ConvertStream HostRenderTarget_Height = XboxRenderTarget_Height; } - bool bNeedRHWTransform = (g_Xbox_MultiSampleType > 0 && g_Xbox_MultiSampleType != XTL::X_D3DMULTISAMPLE_NONE) || (XboxRenderTarget_Width < HostRenderTarget_Width && XboxRenderTarget_Height < HostRenderTarget_Height); + bool bNeedRHWTransform = g_Xbox_MultiSampleType > 0 || (XboxRenderTarget_Width < HostRenderTarget_Width && XboxRenderTarget_Height < HostRenderTarget_Height); for (uint32_t uiVertex = 0; uiVertex < uiVertexCount; uiVertex++) { FLOAT *pVertexDataAsFloat = (FLOAT*)(&pHostVertexData[uiVertex * uiHostVertexStride]); From ba2d18d17f9367ebc4d0455c67be7a2d71c0f382 Mon Sep 17 00:00:00 2001 From: patrickvl Date: Fri, 8 May 2020 12:05:29 +0200 Subject: [PATCH 8/9] Prevent invalid g_Xbox_MultiSampleType values using a setter This avoids zero-dectection later on Some tooling functions to top it off. --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 145 ++++++++++++++-------- src/core/hle/D3D8/XbD3D8Types.h | 47 +++++-- src/core/hle/D3D8/XbVertexBuffer.cpp | 14 +-- 3 files changed, 137 insertions(+), 69 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index a132ccaa5..ae29c2b5d 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -2830,6 +2830,89 @@ ConvertedIndexBuffer& CxbxUpdateActiveIndexBuffer return CacheEntry; } +#define CXBX_D3DMULTISAMPLE_XSCALE(type) (((type) & XTL::X_D3DMULTISAMPLE_XSCALE_MASK) >> XTL::X_D3DMULTISAMPLE_XSCALE_SHIFT) +#define CXBX_D3DMULTISAMPLE_YSCALE(type) (((type) & XTL::X_D3DMULTISAMPLE_YSCALE_MASK) >> XTL::X_D3DMULTISAMPLE_YSCALE_SHIFT) + +void SetXboxMultiSampleType(XTL::X_D3DMULTISAMPLE_TYPE value) +{ + // Validate & correct input, to detect test cases and avoid trouble when using g_Xbox_MultiSampleType : + if (value == 0) { + LOG_TEST_CASE("Correcting zero to X_D3DMULTISAMPLE_NONE"); + value = XTL::X_D3DMULTISAMPLE_NONE; + } + if (value & ~XTL::X_D3DMULTISAMPLE_KNOWN_MASK) { + LOG_TEST_CASE("Removing unknown X_D3DMULTISAMPLE bits"); + value &= XTL::X_D3DMULTISAMPLE_KNOWN_MASK; + } + if (CXBX_D3DMULTISAMPLE_XSCALE(value) == 0) { + LOG_TEST_CASE("Correcting XSCALE 0 to 1"); + value |= 1 << XTL::X_D3DMULTISAMPLE_XSCALE_SHIFT; + } + if (CXBX_D3DMULTISAMPLE_YSCALE(value) == 0) { + LOG_TEST_CASE("Correcting YSCALE 0 to 1"); + value |= 1 << XTL::X_D3DMULTISAMPLE_YSCALE_SHIFT; + } + if ((CXBX_D3DMULTISAMPLE_XSCALE(value) == 1) && (CXBX_D3DMULTISAMPLE_YSCALE(value) == 1)) { + if (value & XTL::X_D3DMULTISAMPLE_ALGO_MASK) { + LOG_TEST_CASE("Removing algorithm for 1 by 1 scale"); + value &= ~XTL::X_D3DMULTISAMPLE_ALGO_MASK; + } + if (value & XTL::X_D3DMULTISAMPLE_SAMPLING_MASK) { + LOG_TEST_CASE("Removing sampling for 1 by 1 scale"); + value &= ~XTL::X_D3DMULTISAMPLE_SAMPLING_MASK; + } + } + if (value == XTL::X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN) { + LOG_TEST_CASE("X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN"); // This used to use scale 1.5f + } + // Set the now-validated g_Xbox_MultiSampleType value : + g_Xbox_MultiSampleType = value; +} + +static inline float GetMultiSampleOffsetDelta() +{ + // TODO : What impact does X_D3DMULTISAMPLE_SAMPLING_SUPER have on offset? + return (g_Xbox_MultiSampleType & XTL::X_D3DMULTISAMPLE_SAMPLING_MULTI) ? 0.0f : 0.5f; +} + +static inline void GetMultiSampleOffset(float& xOffset, float& yOffset) +{ + xOffset = yOffset = GetMultiSampleOffsetDelta(); +} + +static inline void GetMultiSampleScale(float& xScale, float& yScale) +{ + xScale = (float)CXBX_D3DMULTISAMPLE_XSCALE(g_Xbox_MultiSampleType); + yScale = (float)CXBX_D3DMULTISAMPLE_YSCALE(g_Xbox_MultiSampleType); +} + +void GetMultiSampleOffsetAndScale(float& xScale, float& yScale, float& xOffset, float& yOffset) +{ + GetMultiSampleScale(xScale, yScale); + GetMultiSampleOffset(xOffset, yOffset); +} + +static void ApplyXboxMultiSampleOffset(float& x, float& y) +{ + float d = GetMultiSampleOffsetDelta(); + x += d; + y += d; +} + +static void ApplyXboxMultiSampleScale(float& x, float& y) +{ + float xs, ys; + GetMultiSampleScale(xs, ys); + x *= xs; + y *= ys; +} + +void ApplyXboxMultiSampleOffsetAndScale(float& x, float& y) +{ + ApplyXboxMultiSampleScale(x, y); + ApplyXboxMultiSampleOffset(x, y); +} + void Direct3D_CreateDevice_Start ( XTL::X_D3DPRESENT_PARAMETERS *pPresentationParameters @@ -2843,7 +2926,7 @@ void Direct3D_CreateDevice_Start CxbxKrnlCleanup("Failed to init XboxTextureStates"); } - g_Xbox_MultiSampleType = pPresentationParameters->MultiSampleType; + SetXboxMultiSampleType(pPresentationParameters->MultiSampleType); // create default device *before* calling Xbox Direct3D_CreateDevice trampline // to avoid hitting EMUPATCH'es that need a valid g_pD3DDevice @@ -3085,7 +3168,7 @@ HRESULT WINAPI XTL::EMUPATCH(D3DDevice_Reset) // Instead, it simply re-creates the backbuffer with a new configuration // Store the new multisampling configuration - g_Xbox_MultiSampleType = pPresentationParameters->MultiSampleType; + SetXboxMultiSampleType(pPresentationParameters->MultiSampleType); // Since Reset will call create a new backbuffer surface, we can clear our current association // NOTE: We don't actually free the Xbox data, the Xbox side will do this for us when we call the trampoline below. @@ -3751,44 +3834,6 @@ float GetZScaleForSurface(XTL::X_D3DSurface* pSurface) return 1; } -void GetMultiSampleOffsetAndScale(float& xScale, float& yScale, float& xOffset, float& yOffset) -{ - // Default values - xScale = 1.0f; - yScale = 1.0f; - xOffset = 0.5f; - yOffset = 0.5f; - - // NOTE: Can be 0, so we need to check for that too - if (g_Xbox_MultiSampleType > 0) { - switch (g_Xbox_MultiSampleType) - { - case XTL::X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_LINEAR: - case XTL::X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_QUINCUNX: - case XTL::X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_LINEAR: - case XTL::X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_GAUSSIAN: - xOffset = yOffset = 0.0f; - break; - case XTL::X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN: - xOffset = yOffset = 0.0f; - LOG_TEST_CASE("X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN"); - break; - } - - #define MULTISAMPLE_XSCALE_MASK 0x00f0 - #define MULTISAMPLE_XSCALE_SHIFT 4 - - #define MULTISAMPLE_YSCALE_MASK 0x000f - #define MULTISAMPLE_YSCALE_SHIFT 0 - - #define MULTISAMPLE_XSCALE(type) ((type & MULTISAMPLE_XSCALE_MASK) >> MULTISAMPLE_XSCALE_SHIFT) - #define MULTISAMPLE_YSCALE(type) ((type & MULTISAMPLE_YSCALE_MASK) >> MULTISAMPLE_YSCALE_SHIFT) - - xScale = (float)MULTISAMPLE_XSCALE(g_Xbox_MultiSampleType); - yScale = (float)MULTISAMPLE_YSCALE(g_Xbox_MultiSampleType); - } -} - void GetViewPortOffsetAndScale(float (&vOffset)[4], float(&vScale)[4]) { // Store viewport offset and scale in constant registers @@ -3801,8 +3846,11 @@ void GetViewPortOffsetAndScale(float (&vOffset)[4], float(&vScale)[4]) float xScale, yScale; float xOffset, yOffset; GetMultiSampleOffsetAndScale(xScale, yScale, xOffset, yOffset); - ViewPort.Width /= xScale; - ViewPort.Height /= yScale; + // Since Width and Height are DWORD, subtracting MultiSampleOffset 0.0f or 0.5f makes no sense + //ViewPort.Width -= xOffset; + //ViewPort.Height -= yOffset; + ViewPort.Width /= (DWORD)xScale; + ViewPort.Height /= (DWORD)yScale; // Calculate Width/Height scale & offset float scaleWidth = (2.0f / ViewPort.Width) * g_RenderScaleFactor; @@ -3929,12 +3977,12 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_SetViewport) } } - // Apply MSAA scale + // Apply MSAA scale and offset float xScale, yScale; - float xOffset, yOffset; - GetMultiSampleOffsetAndScale(xScale, yScale, xOffset, yOffset); - HostViewPort.Width *= xScale; - HostViewPort.Height *= yScale; + GetMultiSampleScale(xScale, yScale); + HostViewPort.Width *= (DWORD)xScale; + HostViewPort.Height *= (DWORD)yScale; + // Since Width and Height are DWORD, adding GetMultiSampleOffset 0.0f or 0.5f makes no sense HRESULT hRet = g_pD3DDevice->SetViewport(&HostViewPort); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetViewport"); @@ -5036,8 +5084,7 @@ DWORD WINAPI XTL::EMUPATCH(D3DDevice_Swap) // We also need to account for any MSAA which may have enlarged the Xbox Backbuffer float xScale, yScale; - float xOffset, yOffset; - GetMultiSampleOffsetAndScale(xScale, yScale, xOffset, yOffset); + GetMultiSampleScale(xScale, yScale); xScale = (float)BackBufferDesc.Width / ((float)XboxBackBufferWidth / xScale); yScale = (float)BackBufferDesc.Height / ((float)XboxBackBufferHeight / yScale); diff --git a/src/core/hle/D3D8/XbD3D8Types.h b/src/core/hle/D3D8/XbD3D8Types.h index daf566898..63e3b848a 100644 --- a/src/core/hle/D3D8/XbD3D8Types.h +++ b/src/core/hle/D3D8/XbD3D8Types.h @@ -463,18 +463,43 @@ struct X_D3DResource #define X_D3DLOCK_TILED 0x00000040 // Xbox extension #define X_D3DLOCK_READONLY 0x00000080 - -const int X_D3DMULTISAMPLE_NONE = 0x0011; -const int X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_LINEAR = 0x1021; -const int X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_QUINCUNX = 0x1121; +// Multisample modes +const int X_D3DMULTISAMPLE_NONE = 0x0011; +const int X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_LINEAR = 0x1021; +const int X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_QUINCUNX = 0x1121; const int X_D3DMULTISAMPLE_2_SAMPLES_SUPERSAMPLE_HORIZONTAL_LINEAR = 0x2021; -const int X_D3DMULTISAMPLE_2_SAMPLES_SUPERSAMPLE_VERTICAL_LINEAR = 0x2012; -const int X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_LINEAR = 0x1022; -const int X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_GAUSSIAN = 0x1222; -const int X_D3DMULTISAMPLE_4_SAMPLES_SUPERSAMPLE_LINEAR = 0x2022; -const int X_D3DMULTISAMPLE_4_SAMPLES_SUPERSAMPLE_GAUSSIAN = 0x2222; -const int X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN = 0x1233; -const int X_D3DMULTISAMPLE_9_SAMPLES_SUPERSAMPLE_GAUSSIAN = 0x2233; +const int X_D3DMULTISAMPLE_2_SAMPLES_SUPERSAMPLE_VERTICAL_LINEAR = 0x2012; +const int X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_LINEAR = 0x1022; +const int X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_GAUSSIAN = 0x1222; +const int X_D3DMULTISAMPLE_4_SAMPLES_SUPERSAMPLE_LINEAR = 0x2022; +const int X_D3DMULTISAMPLE_4_SAMPLES_SUPERSAMPLE_GAUSSIAN = 0x2222; +const int X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN = 0x1233; +const int X_D3DMULTISAMPLE_9_SAMPLES_SUPERSAMPLE_GAUSSIAN = 0x2233; + +// Multisample masks (Cxbx additions) +const int X_D3DMULTISAMPLE_YSCALE_MASK = 0x0003; +const int X_D3DMULTISAMPLE_YSCALE_SHIFT = 0; + +const int X_D3DMULTISAMPLE_XSCALE_MASK = 0x0030; +const int X_D3DMULTISAMPLE_XSCALE_SHIFT = 4; + +const int X_D3DMULTISAMPLE_ALGO_LINEAR = 0x0000; +const int X_D3DMULTISAMPLE_ALGO_QUINCUNX = 0x0100; +const int X_D3DMULTISAMPLE_ALGO_GAUSSIAN = 0x0200; +const int X_D3DMULTISAMPLE_ALGO_MASK = 0x0300; + +const int X_D3DMULTISAMPLE_SAMPLING_NONE = 0x0000; +const int X_D3DMULTISAMPLE_SAMPLING_MULTI = 0x1000; +const int X_D3DMULTISAMPLE_SAMPLING_SUPER = 0x2000; +const int X_D3DMULTISAMPLE_SAMPLING_MASK = 0x3000; + +const int X_D3DMULTISAMPLE_KNOWN_MASK = 0 + | X_D3DMULTISAMPLE_YSCALE_MASK + | X_D3DMULTISAMPLE_XSCALE_MASK + | X_D3DMULTISAMPLE_ALGO_MASK + | X_D3DMULTISAMPLE_SAMPLING_MASK + ; + struct X_D3DVertexBuffer : public X_D3DResource { diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index 263a5db72..d58d337da 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -61,12 +61,13 @@ XTL::X_STREAMINPUT g_Xbox_SetStreamSource[X_VSH_MAX_STREAMS] = { 0 }; // Note : extern XTL::X_D3DSurface* g_pXbox_RenderTarget; extern XTL::X_D3DSurface* g_pXbox_BackBufferSurface; -extern XTL::X_D3DMULTISAMPLE_TYPE g_Xbox_MultiSampleType; +extern XTL::X_D3DMULTISAMPLE_TYPE g_Xbox_MultiSampleType; + void *GetDataFromXboxResource(XTL::X_D3DResource *pXboxResource); bool GetHostRenderTargetDimensions(DWORD* pHostWidth, DWORD* pHostHeight, IDirect3DSurface* pHostRenderTarget = nullptr); uint32_t GetPixelContainerWidth(XTL::X_D3DPixelContainer* pPixelContainer); uint32_t GetPixelContainerHeight(XTL::X_D3DPixelContainer* pPixelContainer); -void GetMultiSampleOffsetAndScale(float& xScale, float& yScale, float& xOffset, float& yOffset); +void ApplyXboxMultiSampleOffsetAndScale(float& x, float& y); void CxbxPatchedStream::Activate(CxbxDrawContext *pDrawContext, UINT uiStream) const { @@ -669,7 +670,7 @@ void CxbxVertexBufferConverter::ConvertStream HostRenderTarget_Height = XboxRenderTarget_Height; } - bool bNeedRHWTransform = g_Xbox_MultiSampleType > 0 || (XboxRenderTarget_Width < HostRenderTarget_Width && XboxRenderTarget_Height < HostRenderTarget_Height); + bool bNeedRHWTransform = (g_Xbox_MultiSampleType > XTL::X_D3DMULTISAMPLE_NONE) || (XboxRenderTarget_Width < HostRenderTarget_Width && XboxRenderTarget_Height < HostRenderTarget_Height); for (uint32_t uiVertex = 0; uiVertex < uiVertexCount; uiVertex++) { FLOAT *pVertexDataAsFloat = (FLOAT*)(&pHostVertexData[uiVertex * uiHostVertexStride]); @@ -682,12 +683,7 @@ void CxbxVertexBufferConverter::ConvertStream pVertexDataAsFloat[0] *= g_RenderScaleFactor; pVertexDataAsFloat[1] *= g_RenderScaleFactor; - // Apply MSAA scale - float xScale, yScale; - float xOffset, yOffset; - GetMultiSampleOffsetAndScale(xScale, yScale, xOffset, yOffset); - pVertexDataAsFloat[0] *= xScale; - pVertexDataAsFloat[1] *= yScale; + ApplyXboxMultiSampleOffsetAndScale(pVertexDataAsFloat[0], pVertexDataAsFloat[1]); } #if 0 From 6d9dfb2dde0c11366a485be42467586c14b58439 Mon Sep 17 00:00:00 2001 From: patrickvl Date: Fri, 8 May 2020 12:13:55 +0200 Subject: [PATCH 9/9] Change zero input into a comment instead of LOG_TEST_CASE everyone into madness --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index ae29c2b5d..86e681e3f 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -2837,7 +2837,7 @@ void SetXboxMultiSampleType(XTL::X_D3DMULTISAMPLE_TYPE value) { // Validate & correct input, to detect test cases and avoid trouble when using g_Xbox_MultiSampleType : if (value == 0) { - LOG_TEST_CASE("Correcting zero to X_D3DMULTISAMPLE_NONE"); + // Correcting zero to X_D3DMULTISAMPLE_NONE value = XTL::X_D3DMULTISAMPLE_NONE; } if (value & ~XTL::X_D3DMULTISAMPLE_KNOWN_MASK) {