GSdx-d3d: Add support for channel shuffle in GSRendererDX.

Split the code from GSRendererDX to GSRendererDX9 and GSRendererDX11. We
ensure d3d9 doesn't blow up with regressions, add required code to
GSRendererDX11 to properly support channel shuffle.

Note the feature is still not yet complete, copy function needs to be
implemented (suggested by Gregory) but it can be done at a later date,
this still fixes a bunch of issues on various games.
This commit is contained in:
lightningterror 2018-12-11 16:18:40 +01:00
parent c7aca64642
commit c511ce80d2
6 changed files with 200 additions and 65 deletions

View File

@ -174,6 +174,145 @@ void GSRendererDX11::EmulateTextureShuffleAndFbmask()
}
}
void GSRendererDX11::EmulateChannelShuffle(GSTexture** rt, const GSTextureCache::Source* tex)
{
// Uncomment to disable HLE emulation (allow to trace the draw call)
// m_channel_shuffle = false;
// First let's check we really have a channel shuffle effect
if (m_channel_shuffle)
{
if (m_game.title == CRC::Tekken5)
{
if (m_context->FRAME.FBW == 1)
{
// Used in stages: Secret Garden, Acid Rain, Moonlit Wilderness
// fprintf(stderr, "Tekken5 RGB Channel\n");
// m_ps_sel.channel = ChannelFetch_RGB;
// m_context->FRAME.FBMSK = 0xFF000000;
// 12 pages: 2 calls by channel, 3 channels, 1 blit
// Minus current draw call
m_skip = 12 * (3 + 3 + 1) - 1;
// *rt = tex->m_from_target;
}
else
{
// Could skip model drawing if wrongly detected
m_channel_shuffle = false;
}
}
else if ((tex->m_texture->GetType() == GSTexture::DepthStencil) && !(tex->m_32_bits_fmt))
{
// So far 2 games hit this code path. Urban Chaos and Tales of Abyss.
// Lacks shader like usual but maybe we can still use it to skip some bad draw calls.
// fprintf(stderr, "Tales Of Abyss Crazyness/Urban Chaos channel not supported\n");
throw GSDXRecoverableError();
}
else if (m_index.tail <= 64 && m_context->CLAMP.WMT == 3)
{
// Blood will tell. I think it is channel effect too but again
// implemented in a different way. I don't want to add more CRC stuff. So
// let's disable channel when the signature is different.
//
// Note: Tales Of Abyss and Tekken5 could hit this path too. Those games are
// handled above.
// fprintf(stderr, "Maybe not a channel!\n");
m_channel_shuffle = false;
}
else if (m_context->CLAMP.WMS == 3 && ((m_context->CLAMP.MAXU & 0x8) == 8))
{
// Read either blue or Alpha. Let's go for Blue ;)
// MGS3/Kill Zone
// fprintf(stderr, "Blue channel\n");
m_ps_sel.channel = ChannelFetch_BLUE;
}
else if (m_context->CLAMP.WMS == 3 && ((m_context->CLAMP.MINU & 0x8) == 0))
{
// Read either Red or Green. Let's check the V coordinate. 0-1 is likely top so
// red. 2-3 is likely bottom so green (actually depends on texture base pointer offset)
bool green = PRIM->FST && (m_vertex.buff[0].V & 32);
if (green && (m_context->FRAME.FBMSK & 0x00FFFFFF) == 0x00FFFFFF)
{
// Typically used in Terminator 3
int blue_mask = m_context->FRAME.FBMSK >> 24;
int green_mask = ~blue_mask & 0xFF;
int blue_shift = -1;
// Note: potentially we could also check the value of the clut
switch (m_context->FRAME.FBMSK >> 24)
{
case 0xFF: ASSERT(0); break;
case 0xFE: blue_shift = 1; break;
case 0xFC: blue_shift = 2; break;
case 0xF8: blue_shift = 3; break;
case 0xF0: blue_shift = 4; break;
case 0xE0: blue_shift = 5; break;
case 0xC0: blue_shift = 6; break;
case 0x80: blue_shift = 7; break;
default: ASSERT(0); break;
}
int green_shift = 8 - blue_shift;
ps_cb.ChannelShuffle = GSVector4i(blue_mask, blue_shift, green_mask, green_shift);
if (blue_shift >= 0)
{
// fprintf(stderr, "Green/Blue channel (%d, %d)\n", blue_shift, green_shift);
m_ps_sel.channel = ChannelFetch_GXBY;
m_context->FRAME.FBMSK = 0x00FFFFFF;
}
else
{
// fprintf(stderr, "Green channel (wrong mask) (fbmask %x)\n", m_context->FRAME.FBMSK >> 24);
m_ps_sel.channel = ChannelFetch_GREEN;
}
}
else if (green)
{
// fprintf(stderr, "Green channel\n");
m_ps_sel.channel = ChannelFetch_GREEN;
}
else
{
// Pop
// fprintf(stderr, "Red channel\n");
m_ps_sel.channel = ChannelFetch_RED;
}
}
else
{
// fprintf(stderr, "Channel not supported\n");
m_channel_shuffle = false;
}
}
// Effect is really a channel shuffle effect so let's cheat a little
if (m_channel_shuffle)
{
dev->PSSetShaderResource(4, tex->m_from_target);
// Replace current draw with a fullscreen sprite
//
// Performance GPU note: it could be wise to reduce the size to
// the rendered size of the framebuffer
GSVertex* s = &m_vertex.buff[0];
s[0].XYZ.X = (uint16)(m_context->XYOFFSET.OFX + 0);
s[1].XYZ.X = (uint16)(m_context->XYOFFSET.OFX + 16384);
s[0].XYZ.Y = (uint16)(m_context->XYOFFSET.OFY + 0);
s[1].XYZ.Y = (uint16)(m_context->XYOFFSET.OFY + 16384);
m_vertex.head = m_vertex.tail = m_vertex.next = 2;
m_index.tail = 2;
}
else
{
#ifdef _DEBUG
dev->PSSetShaderResource(4, NULL);
#endif
}
}
void GSRendererDX11::SetupIA(const float& sx, const float& sy)
{
GSDevice11* dev = (GSDevice11*)m_dev;

View File

@ -29,6 +29,7 @@ class GSRendererDX11 : public GSRendererDX
{
protected:
void EmulateTextureShuffleAndFbmask();
void EmulateChannelShuffle(GSTexture** rt, const GSTextureCache::Source* tex);
void SetupIA(const float& sx, const float& sy);
public:

View File

@ -56,6 +56,61 @@ bool GSRendererDX9::CreateDevice(GSDevice* dev)
return true;
}
void GSRendererDX9::EmulateChannelShuffle(GSTexture** rt, const GSTextureCache::Source* tex)
{
// Channel shuffle will never be supported on Direct3D9 through shaders so just
// use code that skips the bad draw calls.
if (m_channel_shuffle)
{
if (m_game.title == CRC::Tekken5)
{
if (m_context->FRAME.FBW == 1)
{
// Used in stages: Secret Garden, Acid Rain, Moonlit Wilderness
// 12 pages: 2 calls by channel, 3 channels, 1 blit
// Minus current draw call
m_skip = 12 * (3 + 3 + 1) - 1;
}
else
{
// Could skip model drawing if wrongly detected
m_channel_shuffle = false;
}
}
else if ((tex->m_texture->GetType() == GSTexture::DepthStencil) && !(tex->m_32_bits_fmt))
{
// So far 2 games hit this code path. Urban Chaos and Tales of Abyss.
throw GSDXRecoverableError();
}
else if (m_index.tail <= 64 && m_context->CLAMP.WMT == 3)
{
// Blood will tell. I think it is channel effect too but again
// implemented in a different way. I don't want to add more CRC stuff. So
// let's disable channel when the signature is different.
//
// Note: Tales Of Abyss and Tekken5 could hit this path too. Those games are
// handled above.
m_channel_shuffle = false;
}
else if (m_context->CLAMP.WMS == 3 && ((m_context->CLAMP.MAXU & 0x8) == 8))
{
// Read either blue or Alpha.
// MGS3/Kill Zone
throw GSDXRecoverableError();
}
else if (m_context->CLAMP.WMS == 3 && ((m_context->CLAMP.MINU & 0x8) == 0))
{
// Read either Red or Green.
// Terminator 3
throw GSDXRecoverableError();
}
else
{
m_channel_shuffle = false;
}
}
}
void GSRendererDX9::EmulateTextureShuffleAndFbmask()
{
if (m_texture_shuffle)

View File

@ -35,6 +35,7 @@ protected:
} m_fba;
void EmulateTextureShuffleAndFbmask();
void EmulateChannelShuffle(GSTexture** rt, const GSTextureCache::Source* tex);
void SetupIA(const float& sx, const float& sy);
void UpdateFBA(GSTexture* rt);

View File

@ -156,68 +156,6 @@ void GSRendererDX::EmulateZbuffer()
}
}
void GSRendererDX::EmulateChannelShuffle(const GSTextureCache::Source* tex)
{
// Channel shuffle effect not supported on DX. Let's keep the logic because it help to
// reduce memory requirement (and why not a partial port)
// Uncomment to disable (allow to trace the draw call)
// m_channel_shuffle = false;
// First let's check we really have a channel shuffle effect
if (m_channel_shuffle)
{
if (m_game.title == CRC::Tekken5)
{
if (m_context->FRAME.FBW == 1)
{
// Used in stages: Secret Garden, Acid Rain, Moonlit Wilderness
// Skip channel effect, it misses a shader for proper screen effect but at least the top left screen issue isn't appearing anymore
// 12 pages: 2 calls by channel, 3 channels, 1 blit
// Minus current draw call
m_skip = 12 * (3 + 3 + 1) - 1;
}
else
{
// Could skip model drawing if wrongly detected
m_channel_shuffle = false;
}
}
else if ((tex->m_texture->GetType() == GSTexture::DepthStencil) && !(tex->m_32_bits_fmt))
{
// So far 2 games hit this code path. Urban Chaos and Tales of Abyss.
// Lacks shader like usual but maybe we can still use it to skip some bad draw calls.
throw GSDXRecoverableError();
}
else if (m_index.tail <= 64 && m_context->CLAMP.WMT == 3)
{
// Blood will tell. I think it is channel effect too but again
// implemented in a different way. I don't want to add more CRC stuff. So
// let's disable channel when the signature is different.
//
// Note: Tales Of Abyss and Tekken5 could hit this path too. Those games are
// handled above.
m_channel_shuffle = false;
}
else if (m_context->CLAMP.WMS == 3 && ((m_context->CLAMP.MAXU & 0x8) == 8))
{
// Read either blue or Alpha. Let's go for Blue ;)
// MGS3/Kill Zone
throw GSDXRecoverableError();
}
else if (m_context->CLAMP.WMS == 3 && ((m_context->CLAMP.MINU & 0x8) == 0))
{
// Read either Red or Green. Let's check the V coordinate. 0-1 is likely top so
// red. 2-3 is likely bottom so green (actually depends on texture base pointer offset)
throw GSDXRecoverableError();
}
else
{
m_channel_shuffle = false;
}
}
}
void GSRendererDX::EmulateTextureSampler(const GSTextureCache::Source* tex)
{
const GSLocalMemory::psm_t &psm = GSLocalMemory::m_psm[m_context->TEX0.PSM];
@ -402,7 +340,7 @@ void GSRendererDX::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
// HLE implementation of the channel selection effect
//
// Warning it must be done at the begining because it will change the vertex list
EmulateChannelShuffle(tex);
EmulateChannelShuffle(&rt, tex);
// Upscaling hack to avoid various line/grid issues
MergeSprite(tex);
@ -729,7 +667,8 @@ void GSRendererDX::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
}
// rs
GSVector4i scissor = GSVector4i(GSVector4(rtscale).xyxy() * m_context->scissor.in).rintersect(GSVector4i(rtsize).zwxy());
const GSVector4& hacked_scissor = m_channel_shuffle ? GSVector4(0, 0, 1024, 1024) : m_context->scissor.in;
GSVector4i scissor = GSVector4i(GSVector4(rtscale).xyxy() * hacked_scissor).rintersect(GSVector4i(rtsize).zwxy());
dev->OMSetRenderTargets(rt, ds, &scissor);
dev->PSSetShaderResource(0, tex ? tex->m_texture : NULL);

View File

@ -37,10 +37,10 @@ protected:
void ResetStates();
void EmulateAtst(const int pass, const GSTextureCache::Source* tex);
void EmulateZbuffer();
void EmulateChannelShuffle(const GSTextureCache::Source* tex);
void EmulateTextureSampler(const GSTextureCache::Source* tex);
virtual void DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex);
virtual void EmulateTextureShuffleAndFbmask() = 0;
virtual void EmulateChannelShuffle(GSTexture** rt, const GSTextureCache::Source* tex) = 0;
virtual void SetupIA(const float& sx, const float& sy) = 0;
virtual void UpdateFBA(GSTexture* rt) {}