From 79b5f2154f916fd76a3bb3dd1a7a0899c6767d48 Mon Sep 17 00:00:00 2001 From: sideprojectslab Date: Fri, 11 Nov 2022 15:13:45 +0100 Subject: [PATCH] GS: added Motion Adaptive Deinterlacing support for Metal renderer --- pcsx2/Docs/GameIndex.md | 2 +- pcsx2/GS/Renderers/Metal/GSDeviceMTL.h | 2 +- pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm | 9 +- pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h | 2 +- pcsx2/GS/Renderers/Metal/interlace.metal | 126 ++++++++++++++++++- 5 files changed, 132 insertions(+), 9 deletions(-) diff --git a/pcsx2/Docs/GameIndex.md b/pcsx2/Docs/GameIndex.md index d0360ee2e0..7005dcf5e5 100644 --- a/pcsx2/Docs/GameIndex.md +++ b/pcsx2/Docs/GameIndex.md @@ -150,7 +150,7 @@ The clamp modes are also numerically based. Default: None (`0`) * deinterlace [Value between `0` to `9`] - {Automatic Off, WeaveTFF, WeaveBFF, BobTFF, BobBFF, BlendTFF, BlendBFF, AdaptiveTFF, AdaptiveBFF} + {Automatic, Off, WeaveTFF, WeaveBFF, BobTFF, BobBFF, BlendTFF, BlendBFF, AdaptiveTFF, AdaptiveBFF} Default: Automatic (No value, looks up GameDB) ### GS Hardware Renderer Fixes diff --git a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h index 81e76724b6..f71cc0b1f4 100644 --- a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h +++ b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h @@ -347,7 +347,7 @@ public: GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override; void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) override; - void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset) override; + void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx) override; void DoFXAA(GSTexture* sTex, GSTexture* dTex) override; void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) override; void DoExternalFX(GSTexture* sTex, GSTexture* dTex) override; diff --git a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm index 7839017090..49e69514e9 100644 --- a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm +++ b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm @@ -583,18 +583,19 @@ void GSDeviceMTL::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, StretchRect(dTex, full_r, sTex[2], dRect[0], ShaderConvert::YUV); }} -void GSDeviceMTL::DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset) +void GSDeviceMTL::DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx) { @autoreleasepool { id cmdbuf = GetRenderCmdBuf(); GSScopedDebugGroupMTL dbg(cmdbuf, @"DoInterlace"); - GSVector4 s = GSVector4(dTex->GetSize()); + GSVector4 ss = GSVector4(sTex->GetSize()); + GSVector4 ds = GSVector4(dTex->GetSize()); GSVector4 sRect(0, 0, 1, 1); - GSVector4 dRect(0.f, yoffset, s.x, s.y + yoffset); + GSVector4 dRect(0.f, yoffset, ds.x, ds.y + yoffset); GSMTLInterlacePSUniform cb = {}; - cb.ZrH = {0, 1.f / s.y}; + cb.ZrH = {static_cast(bufIdx), 1.0f / ss.y, ss.y, MAD_SENSITIVITY}; DoStretchRect(sTex, sRect, dTex, dRect, m_interlace_pipeline[shader], linear, shader > 1 ? LoadAction::DontCareIfFull : LoadAction::Load, &cb, sizeof(cb)); }} diff --git a/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h b/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h index ec49c121b1..5ff545b8d0 100644 --- a/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h +++ b/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h @@ -54,7 +54,7 @@ struct GSMTLPresentPSUniform struct GSMTLInterlacePSUniform { - vector_float2 ZrH; + vector_float4 ZrH; }; struct GSMTLMainVertex diff --git a/pcsx2/GS/Renderers/Metal/interlace.metal b/pcsx2/GS/Renderers/Metal/interlace.metal index 8ab2afe89d..b5842c222c 100644 --- a/pcsx2/GS/Renderers/Metal/interlace.metal +++ b/pcsx2/GS/Renderers/Metal/interlace.metal @@ -36,9 +36,10 @@ fragment float4 ps_interlace1(ConvertShaderData data [[stage_in]], ConvertPSRes fragment float4 ps_interlace2(ConvertShaderData data [[stage_in]], ConvertPSRes res, constant GSMTLInterlacePSUniform& uniform [[buffer(GSMTLBufferIndexUniforms)]]) { - float4 c0 = res.sample(data.t - uniform.ZrH); + float2 vstep = float2(0.0f, uniform.ZrH.y); + float4 c0 = res.sample(data.t - vstep); float4 c1 = res.sample(data.t); - float4 c2 = res.sample(data.t + uniform.ZrH); + float4 c2 = res.sample(data.t + vstep); return (c0 + c1 * 2.f + c2) / 4.f; } @@ -47,3 +48,124 @@ fragment float4 ps_interlace3(ConvertShaderData data [[stage_in]], ConvertPSRes return res.sample(data.t); } +fragment float4 ps_interlace4(ConvertShaderData data [[stage_in]], ConvertPSRes res, + constant GSMTLInterlacePSUniform& uniform [[buffer(GSMTLBufferIndexUniforms)]]) +{ + const int vres = int(round(uniform.ZrH.z)); + const int idx = int(round(uniform.ZrH.x)); + const int bank = idx >> 1; + const int field = idx & 1; + const int vpos = int(data.p.y) + (((((vres + 1) >> 1) << 1) - vres) & bank); + const float2 bofs = float2(0.0f, 0.5f * bank); + const float2 vscale = float2(1.0f, 2.0f); + const float2 optr = data.t - bofs; + const float2 iptr = optr * vscale; + + if ((optr.y >= 0.0f) && (optr.y < 0.5f) && ((vpos & 1) == field)) + return res.sample(iptr); + else + discard_fragment(); + + return float4(0.0f, 0.0f, 0.0f, 0.0f); + +} + +fragment float4 ps_interlace5(ConvertShaderData data [[stage_in]], ConvertPSRes res, + constant GSMTLInterlacePSUniform& uniform [[buffer(GSMTLBufferIndexUniforms)]]) +{ + const float sensitivity = uniform.ZrH.w; + const float3 motion_thr = float3(1.0, 1.0, 1.0) * sensitivity; + const float2 vofs = float2(0.0f, 0.5f); + const float2 vscale = float2(1.0f, 0.5f); + const int idx = int(round(uniform.ZrH.x)); + const int bank = idx >> 1; + const int field = idx & 1; + const float2 line_ofs = float2(0.0f, uniform.ZrH.y); + const float2 iptr = data.t * vscale; + + float2 p_new_cf; + float2 p_old_cf; + float2 p_new_af; + float2 p_old_af; + + switch (idx) + { + case 0: + p_new_cf = iptr; + p_new_af = iptr + vofs; + p_old_cf = iptr + vofs; + p_old_af = iptr; + break; + case 1: + p_new_cf = iptr; + p_new_af = iptr; + p_old_cf = iptr + vofs; + p_old_af = iptr + vofs; + break; + case 2: + p_new_cf = iptr + vofs; + p_new_af = iptr; + p_old_cf = iptr; + p_old_af = iptr + vofs; + break; + case 3: + p_new_cf = iptr + vofs; + p_new_af = iptr + vofs; + p_old_cf = iptr; + p_old_af = iptr; + break; + default: + break; + } + + // calculating motion + + float4 hn = res.sample(p_new_cf - line_ofs); // high + float4 cn = res.sample(p_new_af); // center + float4 ln = res.sample(p_new_cf + line_ofs); // low + + float4 ho = res.sample(p_old_cf - line_ofs); // high + float4 co = res.sample(p_old_af); // center + float4 lo = res.sample(p_old_cf + line_ofs); // low + + float3 mh = hn.rgb - ho.rgb; + float3 mc = cn.rgb - co.rgb; + float3 ml = ln.rgb - lo.rgb; + + mh = max(mh, -mh) - motion_thr; + mc = max(mc, -mc) - motion_thr; + ml = max(ml, -ml) - motion_thr; + + +// float mh_max = max(max(mh.x, mh.y), mh.z); +// float mc_max = max(max(mc.x, mc.y), mc.z); +// float ml_max = max(max(ml.x, ml.y), ml.z); + + float mh_max = mh.x + mh.y + mh.z; + float mc_max = mc.x + mc.y + mc.z; + float ml_max = ml.x + ml.y + ml.z; + + // selecting deinterlacing output + + if (((int(data.p.y) & 1) == field)) // output coordinate present on current field + { + return res.sample(p_new_cf); + } + else if ((iptr.y > 0.5f - line_ofs.y) || (iptr.y < 0.0 + line_ofs.y)) + { + return res.sample(p_new_af); + } + else + { + if (((mh_max > 0.0f) || (ml_max > 0.0f)) || (mc_max > 0.0f)) + { + return (hn + ln) / 2.0f; + } + else + { + return res.sample(p_new_af); + } + } + + return float4(0.0f, 0.0f, 0.0f, 0.0f); +}