Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
lightningterror committed Dec 11, 2018
1 parent 9a473f7 commit 4faa9d6
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 65 deletions.
139 changes: 139 additions & 0 deletions plugins/GSdx/Renderers/DX11/GSRendererDX11.cpp
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions plugins/GSdx/Renderers/DX11/GSRendererDX11.h
Expand Up @@ -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:
Expand Down
55 changes: 55 additions & 0 deletions plugins/GSdx/Renderers/DX9/GSRendererDX9.cpp
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions plugins/GSdx/Renderers/DX9/GSRendererDX9.h
Expand Up @@ -35,6 +35,7 @@ class GSRendererDX9 : public GSRendererDX
} m_fba;

void EmulateTextureShuffleAndFbmask();
void EmulateChannelShuffle(GSTexture** rt, const GSTextureCache::Source* tex);
void SetupIA(const float& sx, const float& sy);
void UpdateFBA(GSTexture* rt);

Expand Down
67 changes: 3 additions & 64 deletions plugins/GSdx/Renderers/DXCommon/GSRendererDX.cpp
Expand Up @@ -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];
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion plugins/GSdx/Renderers/DXCommon/GSRendererDX.h
Expand Up @@ -37,10 +37,10 @@ class GSRendererDX : public GSRendererHW
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) {}

Expand Down

0 comments on commit 4faa9d6

Please sign in to comment.