gsdx-d3d11: Partial port of frame buffer masking from opengl.

It works on games such as Fifa Street 1 and 2 (character and stage
rendering), mission impossible operation surma (shadow rendering).
It needs at least Basic level of blending enabled on d3d11.
This commit is contained in:
lightningterror 2019-04-17 22:14:17 +02:00
parent 9d60d6acfd
commit de5e9a85bb
9 changed files with 80 additions and 24 deletions

View File

@ -99,6 +99,7 @@ public:
GSVector4 MinF_TA;
GSVector4i MskFix;
GSVector4i ChannelShuffle;
GSVector4i FbMask;
GSVector4 TC_OffsetHack;
@ -111,6 +112,7 @@ public:
MinF_TA = GSVector4::zero();
MskFix = GSVector4i::zero();
ChannelShuffle = GSVector4i::zero();
FbMask = GSVector4i::zero();
}
__forceinline bool Update(const PSConstantBuffer* cb)
@ -118,7 +120,7 @@ public:
GSVector4i* a = (GSVector4i*)this;
GSVector4i* b = (GSVector4i*)cb;
if(!((a[0] == b[0]) /*& (a[1] == b1)*/ & (a[2] == b[2]) & (a[3] == b[3]) & (a[4] == b[4]) & (a[5] == b[5]) & (a[6] == b[6])).alltrue()) // if WH matches HalfTexel does too
if(!((a[0] == b[0]) /*& (a[1] == b1)*/ & (a[2] == b[2]) & (a[3] == b[3]) & (a[4] == b[4]) & (a[5] == b[5]) & (a[6] == b[6]) & (a[7] == b[7])).alltrue()) // if WH matches HalfTexel does too
{
a[0] = b[0];
a[1] = b[1];
@ -127,6 +129,7 @@ public:
a[4] = b[4];
a[5] = b[5];
a[6] = b[6];
a[7] = b[7];
return true;
}
@ -201,6 +204,7 @@ public:
// Shuffle and fbmask effect
uint32 shuffle:1;
uint32 read_ba:1;
uint32 fbmask:1;
// *** Word 2
// Blend and Colclip
@ -210,7 +214,6 @@ public:
uint32 channel:3;
// Hack
uint32 aout:1;
uint32 spritehack:1;
uint32 tcoffsethack:1;
uint32 urban_chaos_hle:1;

View File

@ -26,13 +26,9 @@ GSRendererDX11::GSRendererDX11()
: GSRendererHW(new GSTextureCache11(this))
{
if (theApp.GetConfigB("UserHacks"))
{
UserHacks_AlphaStencil = theApp.GetConfigB("UserHacks_AlphaStencil");
}
else
{
UserHacks_AlphaStencil = false;
}
ResetStates();
}
@ -244,38 +240,71 @@ void GSRendererDX11::EmulateTextureShuffleAndFbmask()
if (rg_mask != 0xFF)
{
if (write_ba)
{
// fprintf(stderr, "Color shuffle %s => B\n", read_ba ? "B" : "R");
m_om_bsel.wb = 1;
}
else
{
// fprintf(stderr, "Color shuffle %s => R"\n, read_ba ? "B" : "R");
m_om_bsel.wr = 1;
}
else if ((fbmask & 0xFF) != 0xFF)
{
}
#ifdef _DEBUG
fprintf(stderr, "Please fix me! wb %u wr %u\n", m_om_bsel.wb, m_om_bsel.wr);
if (rg_mask)
{
fprintf(stderr, "ERROR: FBMASK SW emulated fb_mask:%x on RG tex shuffle not supported\n", fbmask);
// ASSERT(0);
}
#endif
//ASSERT(0);
}
if (ba_mask != 0xFF)
{
if (write_ba)
{
// fprintf(stderr, "Color shuffle %s => A"\n, read_ba ? "A" : "G");
m_om_bsel.wa = 1;
}
else
{
// fprintf(stderr, "Color shuffle %s => G"\n, read_ba ? "A" : "G");
m_om_bsel.wg = 1;
}
else if ((fbmask & 0xFF) != 0xFF)
{
}
#ifdef _DEBUG
fprintf(stderr, "Please fix me! wa %u wg %u\n", m_om_bsel.wa, m_om_bsel.wg);
if (ba_mask)
{
fprintf(stderr, "ERROR: FBMASK SW emulated fb_mask:%x on BA tex shuffle not supported\n", fbmask);
// ASSERT(0);
}
#endif
//ASSERT(0);
}
}
else
{
m_ps_sel.dfmt = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmt;
m_om_bsel.wrgba = ~GSVector4i::load((int)m_context->FRAME.FBMSK).eq8(GSVector4i::xffffffff()).mask();
GSVector4i fbmask_v = GSVector4i::load((int)m_context->FRAME.FBMSK);
int ff_fbmask = fbmask_v.eq8(GSVector4i::xffffffff()).mask();
int zero_fbmask = fbmask_v.eq8(GSVector4i::zero()).mask();
m_om_bsel.wrgba = ~ff_fbmask; // Enable channel if at least 1 bit is 0
m_ps_sel.fbmask = m_sw_blending && (~ff_fbmask & ~zero_fbmask & 0xF);
if (m_ps_sel.fbmask)
{
ps_cb.FbMask = fbmask_v.u8to32();
// Only alpha is special here, I think we can take a very unsafe shortcut
// Alpha isn't blended on the GS but directly copyied into the RT.
//
// Behavior is clearly undefined however there is a high probability that
// it will work. Masked bit will be constant and normally the same everywhere
// RT/FS output/Cached value.
/*fprintf(stderr, "FBMASK SW emulated alpha fb_mask:%x on %d bits format\n", m_context->FRAME.FBMSK,
(GSLocalMemory::m_psm[m_context->FRAME.PSM].fmt == 2) ? 16 : 32);*/
m_bind_rtsample = true;
}
}
}
@ -621,6 +650,8 @@ void GSRendererDX11::EmulateTextureSampler(const GSTextureCache::Source* tex)
void GSRendererDX11::ResetStates()
{
m_bind_rtsample = false;
m_vs_sel.key = 0;
m_gs_sel.key = 0;
m_ps_sel.key = 0;
@ -912,6 +943,15 @@ void GSRendererDX11::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sou
m_ps_sel.tfx = 4;
}
if (m_bind_rtsample)
{
// Bind the RT.This way special effect can use it.
// Do not always bind the rt when it's not needed,
// only bind it when effects use it such as fbmask emulation currently
// because we copy the frame buffer and it is quite slow.
dev->PSSetShaderResource(3, rt);
}
if (m_game.title == CRC::ICO)
{
GSVertex* v = &m_vertex.buff[0];

View File

@ -29,6 +29,7 @@ class GSRendererDX11 final : public GSRendererHW
{
private:
bool UserHacks_AlphaStencil;
bool m_bind_rtsample;
private:
inline void ResetStates();

View File

@ -220,7 +220,7 @@ void GSDevice11::SetupPS(PSSelector sel, const PSConstantBuffer* cb, PSSamplerSe
str[8] = format("%d", sel.fog);
str[9] = format("%d", sel.clr1);
str[10] = format("%d", sel.fba);
str[11] = format("%d", sel.aout);
str[11] = format("%d", sel.fbmask);
str[12] = format("%d", sel.ltf);
str[13] = format("%d", sel.spritehack);
str[14] = format("%d", sel.tcoffsethack);
@ -249,7 +249,7 @@ void GSDevice11::SetupPS(PSSelector sel, const PSConstantBuffer* cb, PSSamplerSe
{"PS_FOG", str[8].c_str()},
{"PS_CLR1", str[9].c_str()},
{"PS_FBA", str[10].c_str()},
{"PS_AOUT", str[11].c_str()},
{"PS_FBMASK", str[11].c_str()},
{"PS_LTF", str[12].c_str()},
{"PS_SPRITEHACK", str[13].c_str()},
{"PS_TCOFFSETHACK", str[14].c_str()},

View File

@ -40,6 +40,7 @@ GSRendererHW::GSRendererHW(GSTextureCache* tc)
m_upscale_multiplier = theApp.GetConfigI("upscale_multiplier");
m_large_framebuffer = theApp.GetConfigB("large_framebuffer");
m_accurate_date = theApp.GetConfigI("accurate_date");
m_sw_blending = theApp.GetConfigI("accurate_blending_unit");
if (theApp.GetConfigB("UserHacks")) {
m_userhacks_enabled_gs_mem_clear = !theApp.GetConfigB("UserHacks_Disable_Safe_Features");
m_userHacks_enabled_unscale_ptln = !theApp.GetConfigB("UserHacks_Disable_Safe_Features");

View File

@ -151,6 +151,7 @@ protected:
float m_userhacks_tcoffset_y;
int m_accurate_date;
int m_sw_blending;
bool m_channel_shuffle;

View File

@ -26,7 +26,6 @@
GSRendererOGL::GSRendererOGL()
: GSRendererHW(new GSTextureCacheOGL(this))
{
m_sw_blending = theApp.GetConfigI("accurate_blending_unit");
if (theApp.GetConfigB("UserHacks"))
UserHacks_tri_filter = static_cast<TriFiltering>(theApp.GetConfigI("UserHacks_TriFilter"));
else

View File

@ -49,7 +49,6 @@ class GSRendererOGL final : public GSRendererHW
};
private:
int m_sw_blending;
PRIM_OVERLAP m_prim_overlap;
std::vector<size_t> m_drawlist;

View File

@ -29,7 +29,7 @@
#define PS_FOG 0
#define PS_CLR1 0
#define PS_FBA 0
#define PS_AOUT 0
#define PS_FBMASK 0
#define PS_LTF 1
#define PS_SPRITEHACK 0
#define PS_TCOFFSETHACK 0
@ -81,6 +81,7 @@ struct PS_OUTPUT
Texture2D<float4> Texture : register(t0);
Texture2D<float4> Palette : register(t1);
Texture2D<float4> RtSample : register(t3);
Texture2D<float4> RawTexture : register(t4);
SamplerState TextureSampler : register(s0);
SamplerState PaletteSampler : register(s1);
@ -103,6 +104,7 @@ cbuffer cb1
float2 TA;
uint4 MskFix;
int4 ChannelShuffle;
uint4 FbMask;
float4 TC_OffsetHack;
};
@ -902,9 +904,10 @@ PS_OUTPUT ps_main(PS_INPUT input)
}
}
output.c1 = c.a * 255.0f / 128.0f; // used for alpha blending
// Must be done before alpha correction
float alpha_blend = c.a * 255.0f / 128.0f;
if ((PS_DFMT == FMT_16) || PS_AOUT) // 16 bit output
if (PS_DFMT == FMT_16) // 16 bit output
{
float a = 128.0f / 255; // alpha output will be 0x80
@ -915,7 +918,16 @@ PS_OUTPUT ps_main(PS_INPUT input)
if (c.a < 128.0f / 255.0f) c.a += 128.0f / 255.0f;
}
if (PS_FBMASK)
{
float4 rt = RtSample.Load(int3(input.p.xy, 0));
uint4 denorm_rt = uint4(rt * 255.0f + 0.5f);
uint4 denorm_c = uint4(c * 255.0f + 0.5f);
c = float4((denorm_c & ~FbMask) | (denorm_rt & FbMask)) / 255.0f;
}
output.c0 = c;
output.c1 = (float4)(alpha_blend);
return output;
}