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
This commit is contained in:
sideprojectslab 2022-11-05 21:35:06 +01:00 committed by refractionpcsx2
parent e1f7fe5700
commit f10e7f4ab7
14 changed files with 448 additions and 65 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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<float>(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;

View File

@ -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<u8, 16> 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) {}

View File

@ -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<float>(bufIdx), 1.0f / ss.y, ss.y, MAD_SENSITIVITY);
m_ctx->UpdateSubresource(m_interlace.cb.get(), 0, nullptr, &cb, 0, 0);

View File

@ -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<ID3D11PixelShader> ps[4];
wil::com_ptr_nothrow<ID3D11PixelShader> ps[NUM_INTERLACE_SHADERS];
wil::com_ptr_nothrow<ID3D11Buffer> cb;
} m_interlace;

View File

@ -720,22 +720,24 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
static_cast<GSTexture12*>(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<float>(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<GSTexture12*>(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();

View File

@ -156,7 +156,7 @@ private:
std::array<ComPtr<ID3D12PipelineState>, static_cast<int>(PresentShader::Count)> m_present{};
std::array<ComPtr<ID3D12PipelineState>, 16> m_color_copy{};
std::array<ComPtr<ID3D12PipelineState>, 2> m_merge{};
std::array<ComPtr<ID3D12PipelineState>, 4> m_interlace{};
std::array<ComPtr<ID3D12PipelineState>, NUM_INTERLACE_SHADERS> m_interlace{};
std::array<ComPtr<ID3D12PipelineState>, 2> m_hdr_setup_pipelines{}; // [depth]
std::array<ComPtr<ID3D12PipelineState>, 2> m_hdr_finish_pipelines{}; // [depth]
std::array<std::array<ComPtr<ID3D12PipelineState>, 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;

View File

@ -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:

View File

@ -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);
}

View File

@ -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;

View File

@ -855,22 +855,24 @@ void GSDeviceVK::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
static_cast<GSTextureVK*>(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<float>(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<GSTextureVK*>(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);

View File

@ -120,7 +120,7 @@ private:
std::array<VkPipeline, static_cast<int>(PresentShader::Count)> m_present{};
std::array<VkPipeline, 16> m_color_copy{};
std::array<VkPipeline, 2> m_merge{};
std::array<VkPipeline, 4> m_interlace{};
std::array<VkPipeline, NUM_INTERLACE_SHADERS> 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;