From 3e77cb147552cfca786dbea4f7ce0e4195afd84a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 18 Dec 2022 11:01:05 -0800 Subject: [PATCH 1/2] D3D9: Support old-style user clip planes. This fixes negative Z issues on D3D9 in many cases, such as #14168 and #16574, but only when clip planes are supported. --- Common/GPU/D3D9/D3D9StateCache.h | 1 + Common/GPU/D3D9/thin3d_d3d9.cpp | 4 ++++ Common/GPU/thin3d.h | 3 +++ GPU/Directx9/ShaderManagerDX9.cpp | 6 ++++++ GPU/Directx9/StateMappingDX9.cpp | 6 ++++++ 5 files changed, 20 insertions(+) diff --git a/Common/GPU/D3D9/D3D9StateCache.h b/Common/GPU/D3D9/D3D9StateCache.h index 1478442c0f94..32c66921153c 100644 --- a/Common/GPU/D3D9/D3D9StateCache.h +++ b/Common/GPU/D3D9/D3D9StateCache.h @@ -361,6 +361,7 @@ class DirectXState { DxState1 cullMode; DxState1 shadeMode; + DxState1 clipPlaneEnable; BoolState depthTest; diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index a4d668e3692c..e967b5082b94 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -177,6 +177,8 @@ class D3D9RasterState : public RasterState { void Apply(LPDIRECT3DDEVICE9 device) { dxstate.cullMode.set(cullMode); dxstate.scissorTest.enable(); + // Force user clipping off. + dxstate.clipPlaneEnable.set(0); } }; @@ -766,6 +768,8 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID caps_.isTilingGPU = false; caps_.multiSampleLevelsMask = 1; // More could be supported with some work. + caps_.clipPlanesSupported = caps.MaxUserClipPlanes; + if ((caps.RasterCaps & D3DPRASTERCAPS_ANISOTROPY) != 0 && caps.MaxAnisotropy > 1) { caps_.anisoSupported = true; } diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index e5b65d780dec..038e0b4a5197 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -580,6 +580,9 @@ struct DeviceCaps { // From the other backends, we can detect if D3D9 support is known bad (like on Xe) and disable it. bool supportsD3D9; + // Old style, for older GL or Direct3D 9. + u32 clipPlanesSupported; + u32 multiSampleLevelsMask; // Bit n is set if (1 << n) is a valid multisample level. Bit 0 is always set. std::string deviceName; // The device name to use when creating the thin3d context, to get the same one. }; diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 27a42d809bc3..39e8c65bd7bf 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -452,6 +452,12 @@ void ShaderManagerDX9::VSUpdateUniforms(u64 dirtyUniforms) { float data[4] = { viewZScale, viewZCenter, reverseTranslate, reverseScale }; VSSetFloatUniform4(CONST_VS_DEPTHRANGE, data); + + if (draw_->GetDeviceCaps().clipPlanesSupported >= 1) { + float clip[4] = { 0.0f, 0.0f, reverseScale, 1.0f - reverseTranslate * reverseScale }; + // Well, not a uniform, but we treat it as one like other backends. + device_->SetClipPlane(0, clip); + } } if (dirtyUniforms & DIRTY_CULLRANGE) { float minValues[4], maxValues[4]; diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index 3a39a9b27e9d..a03ecc667e26 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -201,6 +201,12 @@ void DrawEngineDX9::ApplyDrawState(int prim) { } else { dxstate.shadeMode.set(gstate.getShadeMode() == GE_SHADE_GOURAUD ? D3DSHADE_GOURAUD : D3DSHADE_FLAT); } + + // We use fixed-function user clipping on D3D9, where available, for negative Z clipping. + if (draw_->GetDeviceCaps().clipPlanesSupported >= 1) { + bool wantClip = !gstate.isModeThrough(); + dxstate.clipPlaneEnable.set(wantClip ? 1 : 0); + } } if (gstate_c.IsDirty(DIRTY_DEPTHSTENCIL_STATE)) { From f6980b9f9dc3d04cf9971ae0b0079eb12c112e3d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 18 Dec 2022 12:01:28 -0800 Subject: [PATCH 2/2] D3D9: Avoid curve Z clip to be safe. I think it does actually happen, but we don't do it in the shader, so let's not do it here. --- GPU/Directx9/StateMappingDX9.cpp | 2 +- GPU/GPUCommon.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index a03ecc667e26..b97aeec37abd 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -204,7 +204,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) { // We use fixed-function user clipping on D3D9, where available, for negative Z clipping. if (draw_->GetDeviceCaps().clipPlanesSupported >= 1) { - bool wantClip = !gstate.isModeThrough(); + bool wantClip = !gstate.isModeThrough() && gstate_c.submitType == SubmitType::DRAW; dxstate.clipPlaneEnable.set(wantClip ? 1 : 0); } } diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 3727f9fcf0fd..48094a17e426 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -2115,7 +2115,7 @@ void GPUCommon::Execute_Bezier(u32 op, u32 diff) { SetDrawType(DRAW_BEZIER, PatchPrimToPrim(surface.primType)); - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); + gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); if (drawEngineCommon_->CanUseHardwareTessellation(surface.primType)) { gstate_c.submitType = SubmitType::HW_BEZIER; if (gstate_c.spline_num_points_u != surface.num_points_u) { @@ -2130,7 +2130,7 @@ void GPUCommon::Execute_Bezier(u32 op, u32 diff) { UpdateUVScaleOffset(); drawEngineCommon_->SubmitCurve(control_points, indices, surface, gstate.vertType, &bytesRead, "bezier"); - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); + gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); gstate_c.submitType = SubmitType::DRAW; // After drawing, we advance pointers - see SubmitPrim which does the same. @@ -2190,7 +2190,7 @@ void GPUCommon::Execute_Spline(u32 op, u32 diff) { SetDrawType(DRAW_SPLINE, PatchPrimToPrim(surface.primType)); - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); + gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); if (drawEngineCommon_->CanUseHardwareTessellation(surface.primType)) { gstate_c.submitType = SubmitType::HW_SPLINE; if (gstate_c.spline_num_points_u != surface.num_points_u) { @@ -2205,7 +2205,7 @@ void GPUCommon::Execute_Spline(u32 op, u32 diff) { UpdateUVScaleOffset(); drawEngineCommon_->SubmitCurve(control_points, indices, surface, gstate.vertType, &bytesRead, "spline"); - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); + gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); gstate_c.submitType = SubmitType::DRAW; // After drawing, we advance pointers - see SubmitPrim which does the same.