From f10e7f4ab7d7bdc4af5bfeee36591ba91076a1d6 Mon Sep 17 00:00:00 2001 From: sideprojectslab Date: Sat, 5 Nov 2022 21:35:06 +0100 Subject: [PATCH] GS: Implemented Motion Adaptive Deinterlacing for all renderers implemented FastMAD motion-adaptive deinterlacing for OpenGL renderer, other renderers will crash. FastMAD is replacing blend (either mode) so select blend to activate MAD under the hood fixed an assert and assessed that one MUST select mode Blend bottom field first to enable MAD removed forced mode 2, added separate motion thresholds for current field and alternate field motion and optimized MAD for Top-FIeld-First mode committing kind-of broken status for review the algorithm works well on most games, but somehow Kingdom Hearts works at half resolution completely fixed weird artifacts on MAD, I only need to fix a 1-line offset that is causing the top of the screen to flicker fixed flicker on first line, I still need to fine-tune some coefficients solved all nastiness by realizing that MAD MUST work on an even resolution, so odd resolutions are rounded up. Now all games I tried look great made MAD sensitivity adjustable inside GDDevice.h and passed to shaders as a parameters. For this purpose ZrH is now a vec4 to hold more parameters conveniently ported MAD to DX11 and DX12 removed rounding of texture size to closest multiple of 2 and fized odd number of lines inside the shaders by also passing the vertical resolution as a parameter improved compatibility of upper buffer offset adjustment for odd resolutions added Vulkan support --- bin/resources/shaders/dx11/interlace.fx | 131 +++++++++++++++++++- bin/resources/shaders/opengl/interlace.glsl | 125 ++++++++++++++++++- bin/resources/shaders/vulkan/interlace.glsl | 130 ++++++++++++++++++- pcsx2/GS/Renderers/Common/GSDevice.cpp | 52 ++++---- pcsx2/GS/Renderers/Common/GSDevice.h | 11 +- pcsx2/GS/Renderers/DX11/GSDevice11.cpp | 9 +- pcsx2/GS/Renderers/DX11/GSDevice11.h | 4 +- pcsx2/GS/Renderers/DX12/GSDevice12.cpp | 14 ++- pcsx2/GS/Renderers/DX12/GSDevice12.h | 4 +- pcsx2/GS/Renderers/Null/GSDeviceNull.h | 2 +- pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp | 9 +- pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h | 4 +- pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp | 14 ++- pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h | 4 +- 14 files changed, 448 insertions(+), 65 deletions(-) diff --git a/bin/resources/shaders/dx11/interlace.fx b/bin/resources/shaders/dx11/interlace.fx index 825751b659..1a0fd66618 100644 --- a/bin/resources/shaders/dx11/interlace.fx +++ b/bin/resources/shaders/dx11/interlace.fx @@ -5,7 +5,7 @@ SamplerState Sampler; cbuffer cb0 { - float2 ZrH; + float4 ZrH; }; struct PS_INPUT @@ -32,9 +32,10 @@ float4 ps_main1(PS_INPUT input) : SV_Target0 float4 ps_main2(PS_INPUT input) : SV_Target0 { - float4 c0 = Texture.Sample(Sampler, input.t - ZrH); + float2 vstep = float2(0.0f, ZrH.y); + float4 c0 = Texture.Sample(Sampler, input.t - vstep); float4 c1 = Texture.Sample(Sampler, input.t); - float4 c2 = Texture.Sample(Sampler, input.t + ZrH); + float4 c2 = Texture.Sample(Sampler, input.t + vstep); return (c0 + c1 * 2 + c2) / 4; } @@ -43,4 +44,128 @@ float4 ps_main3(PS_INPUT input) : SV_Target0 { return Texture.Sample(Sampler, input.t); } + + +float4 ps_main4(PS_INPUT input) : SV_Target0 +{ + const int vres = int(round(ZrH.z)); + const int idx = int(round(ZrH.x)); + const int bank = idx >> 1; + const int field = idx & 1; + const int vpos = int(input.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 = input.t - bofs; + const float2 iptr = optr * vscale; + + if ((optr.y >= 0.0f) && (optr.y < 0.5f) && ((vpos & 1) == field)) + return Texture.Sample(Sampler, iptr); + else + discard; + + return float4(0.0f, 0.0f, 0.0f, 0.0f); + +} + + +float4 ps_main5(PS_INPUT input) : SV_Target0 +{ + const float sensitivity = 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(ZrH.x)); + const int bank = idx >> 1; + const int field = idx & 1; + const float2 line_ofs = float2(0.0f, ZrH.y); + const float2 iptr = input.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 = Texture.Sample(Sampler, p_new_cf - line_ofs); // high + float4 cn = Texture.Sample(Sampler, p_new_af); // center + float4 ln = Texture.Sample(Sampler, p_new_cf + line_ofs); // low + + float4 ho = Texture.Sample(Sampler, p_old_cf - line_ofs); // high + float4 co = Texture.Sample(Sampler, p_old_af); // center + float4 lo = Texture.Sample(Sampler, 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(input.p.y) & 1) == field)) // output coordinate present on current field + { + return Texture.Sample(Sampler, p_new_cf); + } + else if ((iptr.y > 0.5f - line_ofs.y) || (iptr.y < 0.0 + line_ofs.y)) + { + return Texture.Sample(Sampler, p_new_af); + } + else + { + if (((mh_max > 0.0f) || (ml_max > 0.0f)) || (mc_max > 0.0f)) + { + return (hn + ln) / 2.0f; + } + else + { + return Texture.Sample(Sampler, p_new_af); + } + } + + return float4(0.0f, 0.0f, 0.0f, 0.0f); +} + + #endif diff --git a/bin/resources/shaders/opengl/interlace.glsl b/bin/resources/shaders/opengl/interlace.glsl index 46682ff7b7..1f24f345f2 100644 --- a/bin/resources/shaders/opengl/interlace.glsl +++ b/bin/resources/shaders/opengl/interlace.glsl @@ -6,7 +6,7 @@ in vec4 PSin_p; in vec2 PSin_t; in vec4 PSin_c; -uniform vec2 ZrH; +uniform vec4 ZrH; layout(location = 0) out vec4 SV_Target0; @@ -35,9 +35,10 @@ void ps_main1() void ps_main2() { - vec4 c0 = texture(TextureSampler, PSin_t - ZrH); + vec2 vstep = vec2(0.0f, ZrH.y); + vec4 c0 = texture(TextureSampler, PSin_t - vstep); vec4 c1 = texture(TextureSampler, PSin_t); - vec4 c2 = texture(TextureSampler, PSin_t + ZrH); + vec4 c2 = texture(TextureSampler, PSin_t + vstep); SV_Target0 = (c0 + c1 * 2.0f + c2) / 4.0f; } @@ -47,4 +48,122 @@ void ps_main3() SV_Target0 = texture(TextureSampler, PSin_t); } + +void ps_main4() +{ + const int vres = int(round(ZrH.z)); + const int idx = int(round(ZrH.x)); + const int bank = idx >> 1; + const int field = idx & 1; + const int vpos = int(gl_FragCoord.y) + (((((vres + 1) >> 1) << 1) - vres) & bank); + const vec2 bofs = vec2(0.0f, 0.5f * bank); + const vec2 vscale = vec2(1.0f, 2.0f); + const vec2 optr = PSin_t - bofs; + const vec2 iptr = optr * vscale; + + if ((optr.y >= 0.0f) && (optr.y < 0.5f) && ((vpos & 1) == field)) + //if ((optr.y >= 0.0f) && (optr.y < 0.5f) && (int(iptr.y * vres) & 1) == field) + SV_Target0 = texture(TextureSampler, iptr); + else + discard; +} + + +void ps_main5() +{ + const float sensitivity = ZrH.w; + const vec3 motion_thr = vec3(1.0, 1.0, 1.0) * sensitivity; + const vec2 vofs = vec2(0.0f, 0.5f); + const vec2 vscale = vec2(1.0f, 0.5f); + const int idx = int(round(ZrH.x)); + const int bank = idx >> 1; + const int field = idx & 1; + const vec2 line_ofs = vec2(0.0f, ZrH.y); + const vec2 iptr = PSin_t * vscale; + + vec2 p_new_cf; + vec2 p_old_cf; + vec2 p_new_af; + vec2 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 + + vec4 hn = texture(TextureSampler, p_new_cf - line_ofs); // high + vec4 cn = texture(TextureSampler, p_new_af); // center + vec4 ln = texture(TextureSampler, p_new_cf + line_ofs); // low + + vec4 ho = texture(TextureSampler, p_old_cf - line_ofs); // high + vec4 co = texture(TextureSampler, p_old_af); // center + vec4 lo = texture(TextureSampler, p_old_cf + line_ofs); // low + + vec3 mh = hn.rgb - ho.rgb; + vec3 mc = cn.rgb - co.rgb; + vec3 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(gl_FragCoord.y) & 1) == field)) // output coordinate present on current field + { + SV_Target0 = texture(TextureSampler, p_new_cf); + + } + else if ((iptr.y > 0.5f - line_ofs.y) || (iptr.y < 0.0 + line_ofs.y)) + { + SV_Target0 = texture(TextureSampler, p_new_af); + } + else + { + if(((mh_max > 0.0f) || (ml_max > 0.0f)) || (mc_max > 0.0f)) + { + SV_Target0 = (hn + ln) / 2.0f; + } + else + { + SV_Target0 = texture(TextureSampler, p_new_af); + } + } +} + #endif diff --git a/bin/resources/shaders/vulkan/interlace.glsl b/bin/resources/shaders/vulkan/interlace.glsl index 1f024eb24f..d3c622d6eb 100644 --- a/bin/resources/shaders/vulkan/interlace.glsl +++ b/bin/resources/shaders/vulkan/interlace.glsl @@ -20,7 +20,7 @@ layout(location = 0) out vec4 o_col0; layout(push_constant) uniform cb0 { - vec2 ZrH; + vec4 ZrH; }; layout(set = 0, binding = 0) uniform sampler2D samp0; @@ -46,9 +46,10 @@ void ps_main1() #ifdef ps_main2 void ps_main2() { - vec4 c0 = texture(samp0, v_tex - ZrH); + vec2 vstep = vec2(0.0f, ZrH.y); + vec4 c0 = texture(samp0, v_tex - vstep); vec4 c1 = texture(samp0, v_tex); - vec4 c2 = texture(samp0, v_tex + ZrH); + vec4 c2 = texture(samp0, v_tex + vstep); o_col0 = (c0 + c1 * 2.0f + c2) / 4.0f; } @@ -61,4 +62,127 @@ void ps_main3() } #endif + +#ifdef ps_main4 +void ps_main4() +{ + const int vres = int(round(ZrH.z)); + const int idx = int(round(ZrH.x)); + const int bank = idx >> 1; + const int field = idx & 1; + const int vpos = int(gl_FragCoord.y) + (((((vres + 1) >> 1) << 1) - vres) & bank); + const vec2 bofs = vec2(0.0f, 0.5f * bank); + const vec2 vscale = vec2(1.0f, 2.0f); + const vec2 optr = v_tex - bofs; + const vec2 iptr = optr * vscale; + + if ((optr.y >= 0.0f) && (optr.y < 0.5f) && ((vpos & 1) == field)) + o_col0 = texture(samp0, iptr); + else + discard; +} +#endif + + +#ifdef ps_main5 +void ps_main5() +{ + const float sensitivity = ZrH.w; + const vec3 motion_thr = vec3(1.0, 1.0, 1.0) * sensitivity; + const vec2 vofs = vec2(0.0f, 0.5f); + const vec2 vscale = vec2(1.0f, 0.5f); + + int idx = int(round(ZrH.x)); + int bank = idx >> 1; + int field = idx & 1; + vec2 line_ofs = vec2(0.0f, ZrH.y); + + vec2 iptr = v_tex * vscale; + vec2 p_new_cf = vec2(0.0f, 0.0f); + vec2 p_old_cf = vec2(0.0f, 0.0f); + vec2 p_new_af = vec2(0.0f, 0.0f); + vec2 p_old_af = vec2(0.0f, 0.0f); + + 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 + + vec4 hn = texture(samp0, p_new_cf - line_ofs); // high + vec4 cn = texture(samp0, p_new_af); // center + vec4 ln = texture(samp0, p_new_cf + line_ofs); // low + + vec4 ho = texture(samp0, p_old_cf - line_ofs); // high + vec4 co = texture(samp0, p_old_af); // center + vec4 lo = texture(samp0, p_old_cf + line_ofs); // low + + vec3 mh = hn.rgb - ho.rgb; + vec3 mc = cn.rgb - co.rgb; + vec3 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(gl_FragCoord.y) & 1) == field)) // output coordinate present on current field + { + o_col0 = texture(samp0, p_new_cf); + + } + else if ((iptr.y > 0.5f - line_ofs.y) || (iptr.y < 0.0 + line_ofs.y)) + { + o_col0 = texture(samp0, p_new_af); + } + else + { + if(((mh_max > 0.0f) || (ml_max > 0.0f)) || (mc_max > 0.0f)) + { + o_col0 = (hn + ln) / 2.0f; + } + else + { + o_col0 = texture(samp0, p_new_af); + } + } +} +#endif + #endif diff --git a/pcsx2/GS/Renderers/Common/GSDevice.cpp b/pcsx2/GS/Renderers/Common/GSDevice.cpp index 8a955e0e0d..c49c7c6801 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.cpp +++ b/pcsx2/GS/Renderers/Common/GSDevice.cpp @@ -86,6 +86,7 @@ GSDevice::~GSDevice() delete m_merge; delete m_weavebob; delete m_blend; + delete m_mad; delete m_target_tmp; } @@ -101,11 +102,13 @@ void GSDevice::Destroy() delete m_merge; delete m_weavebob; delete m_blend; + delete m_mad; delete m_target_tmp; m_merge = nullptr; m_weavebob = nullptr; m_blend = nullptr; + m_mad = nullptr; m_target_tmp = nullptr; m_current = nullptr; // current is special, points to other textures, no need to delete @@ -312,11 +315,13 @@ void GSDevice::ClearCurrent() delete m_merge; delete m_weavebob; delete m_blend; + delete m_mad; delete m_target_tmp; m_merge = nullptr; m_weavebob = nullptr; m_blend = nullptr; + m_mad = nullptr; m_target_tmp = nullptr; } @@ -358,37 +363,40 @@ void GSDevice::Merge(GSTexture* sTex[3], GSVector4* sRect, GSVector4* dRect, con void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffset) { - ResizeTarget(&m_weavebob, ds.x, ds.y); + static int bufIdx = 0; - if (mode == 0 || mode == 2) // weave or blend + if (mode == 0) // weave { - // weave first const float offset = yoffset * static_cast(field); - - DoInterlace(m_merge, m_weavebob, field, false, GSConfig.DisableInterlaceOffset ? 0.0f : offset); - - if (mode == 2) - { - // blend - - ResizeTarget(&m_blend, ds.x, ds.y); - - DoInterlace(m_weavebob, m_blend, 2, false, 0); - - m_current = m_blend; - } - else - { - m_current = m_weavebob; - } + ResizeTarget(&m_weavebob, ds.x, ds.y); + DoInterlace(m_merge, m_weavebob, field, false, GSConfig.DisableInterlaceOffset ? 0.0f : offset, 0); + m_current = m_weavebob; } else if (mode == 1) // bob { // Field is reversed here as we are countering the bounce. - DoInterlace(m_merge, m_weavebob, 3, true, yoffset * (1-field)); - + ResizeTarget(&m_weavebob, ds.x, ds.y); + DoInterlace(m_merge, m_weavebob, 3, true, yoffset * (1-field), 0); m_current = m_weavebob; } + else if (mode == 2) // FastMAD Motion Adaptive Deinterlacing + { + bufIdx++; + bufIdx &= ~(field ^ 1); + bufIdx |= (field); + bufIdx &= 3; + + float offset = (yoffset * field); + offset = GSConfig.DisableInterlaceOffset ? 0.0f : offset; + + ResizeTarget(&m_mad, ds.x, ds.y * 2.0f); + DoInterlace(m_merge, m_mad, 4, false, offset, bufIdx); + ResizeTarget(&m_blend, ds.x, ds.y); + DoInterlace(m_mad, m_blend, 5, false, 0, bufIdx); + + + m_current = m_blend; + } else { m_current = m_merge; diff --git a/pcsx2/GS/Renderers/Common/GSDevice.h b/pcsx2/GS/Renderers/Common/GSDevice.h index 4dd17f6cfa..40aa0ec214 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.h +++ b/pcsx2/GS/Renderers/Common/GSDevice.h @@ -191,9 +191,7 @@ public: class InterlaceConstantBuffer { public: - GSVector2 ZrH; - float _pad[2]; - + GSVector4 ZrH; // data passed to the shader InterlaceConstantBuffer() { memset(this, 0, sizeof(*this)); } }; @@ -739,11 +737,14 @@ private: static const std::array m_replaceDualSrcBlendMap; protected: - static constexpr u32 MAX_POOLED_TEXTURES = 300; + static constexpr int NUM_INTERLACE_SHADERS = 6; + static constexpr float MAD_SENSITIVITY = 0.08f; + static constexpr u32 MAX_POOLED_TEXTURES = 300; GSTexture* m_merge = nullptr; GSTexture* m_weavebob = nullptr; GSTexture* m_blend = nullptr; + GSTexture* m_mad = nullptr; GSTexture* m_target_tmp = nullptr; GSTexture* m_current = nullptr; struct @@ -762,7 +763,7 @@ protected: GSTexture* FetchSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format, bool clear, bool prefer_reuse); virtual void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) = 0; - virtual void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset) = 0; + virtual void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx) = 0; virtual void DoFXAA(GSTexture* sTex, GSTexture* dTex) {} virtual void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) {} virtual void DoExternalFX(GSTexture* sTex, GSTexture* dTex) {} diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp index dfcaae519d..8345a04d8a 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp @@ -823,16 +823,17 @@ void GSDevice11::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, } } -void GSDevice11::DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset) +void GSDevice11::DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx) { - const GSVector4 s = GSVector4(dTex->GetSize()); + const GSVector4 ss = GSVector4(sTex->GetSize()); + const GSVector4 ds = GSVector4(dTex->GetSize()); const GSVector4 sRect(0, 0, 1, 1); - const GSVector4 dRect(0.0f, yoffset, s.x, s.y + yoffset); + const GSVector4 dRect(0.0f, yoffset, ds.x, ds.y + yoffset); InterlaceConstantBuffer cb; - cb.ZrH = GSVector2(0, 1.0f / s.y); + cb.ZrH = GSVector4(static_cast(bufIdx), 1.0f / ss.y, ss.y, MAD_SENSITIVITY); m_ctx->UpdateSubresource(m_interlace.cb.get(), 0, nullptr, &cb, 0, 0); diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.h b/pcsx2/GS/Renderers/DX11/GSDevice11.h index f1f08b6cef..de1b240a3f 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.h +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.h @@ -119,7 +119,7 @@ private: GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) final; void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) final; - void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0) final; + void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0, int bufIdx = 0) final; void DoFXAA(GSTexture* sTex, GSTexture* dTex) final; void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final; void DoExternalFX(GSTexture* sTex, GSTexture* dTex) final; @@ -185,7 +185,7 @@ private: struct { - wil::com_ptr_nothrow ps[4]; + wil::com_ptr_nothrow ps[NUM_INTERLACE_SHADERS]; wil::com_ptr_nothrow cb; } m_interlace; diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp index 22193e42d8..df371257b3 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp @@ -720,22 +720,24 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, static_cast(dTex)->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); } -void GSDevice12::DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset) +void GSDevice12::DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx) { - const GSVector2i size(dTex->GetSize()); - const GSVector4 s = GSVector4(size); + const GSVector4 ss = GSVector4(sTex->GetSize()); + const GSVector4 ds = GSVector4(dTex->GetSize()); + const GSVector2i ds_i(dTex->GetSize()); const GSVector4 sRect(0, 0, 1, 1); - const GSVector4 dRect(0.0f, yoffset, s.x, s.y + yoffset); + const GSVector4 dRect(0.0f, yoffset, ds.x, ds.y + yoffset); InterlaceConstantBuffer cb; - cb.ZrH = GSVector2(0, 1.0f / s.y); + + cb.ZrH = GSVector4(static_cast(bufIdx), 1.0f / ss.y, ss.y, MAD_SENSITIVITY); GL_PUSH("DoInterlace %dx%d Shader:%d Linear:%d", size.x, size.y, shader, linear); static_cast(dTex)->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET); - const GSVector4i rc(0, 0, size.x, size.y); + const GSVector4i rc(0, 0, ds_i.x, ds_i.y); EndRenderPass(); OMSetRenderTargets(dTex, nullptr, rc); SetUtilityRootSignature(); diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.h b/pcsx2/GS/Renderers/DX12/GSDevice12.h index f91ac5fd0d..b255fd3822 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.h +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.h @@ -156,7 +156,7 @@ private: std::array, static_cast(PresentShader::Count)> m_present{}; std::array, 16> m_color_copy{}; std::array, 2> m_merge{}; - std::array, 4> m_interlace{}; + std::array, NUM_INTERLACE_SHADERS> m_interlace{}; std::array, 2> m_hdr_setup_pipelines{}; // [depth] std::array, 2> m_hdr_finish_pipelines{}; // [depth] std::array, 2>, 2> m_date_image_setup_pipelines{}; // [depth][datm] @@ -181,7 +181,7 @@ private: void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) final; - void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0) final; + void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0, int bufIdx = 0) final; void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final; void DoFXAA(GSTexture* sTex, GSTexture* dTex) final; diff --git a/pcsx2/GS/Renderers/Null/GSDeviceNull.h b/pcsx2/GS/Renderers/Null/GSDeviceNull.h index d1ff6f8aa6..b6fe656f85 100644 --- a/pcsx2/GS/Renderers/Null/GSDeviceNull.h +++ b/pcsx2/GS/Renderers/Null/GSDeviceNull.h @@ -24,7 +24,7 @@ private: GSTexture* CreateSurface(GSTexture::Type type, int w, int h, GSTexture::Format format); void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) {} - void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0) {} + void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0, int bufIdx = 0) {} u16 ConvertBlendEnum(u16 generic) { return 0xFFFF; } public: diff --git a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp index 5eb986b825..b716e42313 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp +++ b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp @@ -1385,19 +1385,20 @@ void GSDeviceOGL::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, StretchRect(dTex, full_r, sTex[2], dRect[2], ShaderConvert::YUV); } -void GSDeviceOGL::DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset) +void GSDeviceOGL::DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx) { GL_PUSH("DoInterlace"); OMSetColorMaskState(); - const GSVector4 s = GSVector4(dTex->GetSize()); + const GSVector4 ss = GSVector4(sTex->GetSize()); + const GSVector4 ds = GSVector4(dTex->GetSize()); const GSVector4 sRect(0, 0, 1, 1); - const GSVector4 dRect(0.0f, yoffset, s.x, s.y + yoffset); + const GSVector4 dRect(0.0f, yoffset, ds.x, ds.y + yoffset); m_interlace.ps[shader].Bind(); - m_interlace.ps[shader].Uniform2f(0, 0, 1.0f / s.y); + m_interlace.ps[shader].Uniform4f(0, bufIdx, 1.0f / ss.y, ss.y, MAD_SENSITIVITY); StretchRect(sTex, sRect, dTex, dRect, m_interlace.ps[shader], linear); } diff --git a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h index 328fb187ff..17370c26ff 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h +++ b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.h @@ -239,7 +239,7 @@ private: struct { - GL::Program ps[4]; // program object + GL::Program ps[NUM_INTERLACE_SHADERS]; // program object } m_interlace; struct @@ -300,7 +300,7 @@ private: GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) final; void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) final; - void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0) final; + void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0, int bufIdx = 0) final; void DoFXAA(GSTexture* sTex, GSTexture* dTex) final; void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final; void DoExternalFX(GSTexture* sTex, GSTexture* dTex) final; diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp index 3363f080e5..ac994deb64 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp @@ -855,22 +855,24 @@ void GSDeviceVK::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, static_cast(dTex)->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } -void GSDeviceVK::DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset) +void GSDeviceVK::DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx) { - const GSVector2i size(dTex->GetSize()); - const GSVector4 s = GSVector4(size); + const GSVector4 ss = GSVector4(sTex->GetSize()); + const GSVector4 ds = GSVector4(dTex->GetSize()); + const GSVector2i ds_i(dTex->GetSize()); const GSVector4 sRect(0, 0, 1, 1); - const GSVector4 dRect(0.0f, yoffset, s.x, s.y + yoffset); + const GSVector4 dRect(0.0f, yoffset, ds.x, ds.y + yoffset); InterlaceConstantBuffer cb; - cb.ZrH = GSVector2(0, 1.0f / s.y); + + cb.ZrH = GSVector4(static_cast(bufIdx), 1.0f / ss.y, ss.y, MAD_SENSITIVITY); GL_PUSH("DoInterlace %dx%d Shader:%d Linear:%d", size.x, size.y, shader, linear); static_cast(dTex)->TransitionToLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - const GSVector4i rc(0, 0, size.x, size.y); + const GSVector4i rc(0, 0, ds_i.x, ds_i.y); EndRenderPass(); OMSetRenderTargets(dTex, nullptr, rc, false); SetUtilityTexture(sTex, linear ? m_linear_sampler : m_point_sampler); diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h index 771cf73286..c54ff27cbd 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h @@ -120,7 +120,7 @@ private: std::array(PresentShader::Count)> m_present{}; std::array m_color_copy{}; std::array m_merge{}; - std::array m_interlace{}; + std::array m_interlace{}; VkPipeline m_hdr_setup_pipelines[2][2] = {}; // [depth][feedback_loop] VkPipeline m_hdr_finish_pipelines[2][2] = {}; // [depth][feedback_loop] VkRenderPass m_date_image_setup_render_passes[2][2] = {}; // [depth][clear] @@ -155,7 +155,7 @@ private: void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) final; - void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0) final; + void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0, int bufIdx = 0) final; void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final; void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;