diff --git a/plugins/GSdx/Renderers/DX11/GSRendererDX11.cpp b/plugins/GSdx/Renderers/DX11/GSRendererDX11.cpp index 0c4388567a..88543101e7 100644 --- a/plugins/GSdx/Renderers/DX11/GSRendererDX11.cpp +++ b/plugins/GSdx/Renderers/DX11/GSRendererDX11.cpp @@ -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; diff --git a/plugins/GSdx/Renderers/DX11/GSRendererDX11.h b/plugins/GSdx/Renderers/DX11/GSRendererDX11.h index eecf52bc70..a495352b6f 100644 --- a/plugins/GSdx/Renderers/DX11/GSRendererDX11.h +++ b/plugins/GSdx/Renderers/DX11/GSRendererDX11.h @@ -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: diff --git a/plugins/GSdx/Renderers/DX9/GSRendererDX9.cpp b/plugins/GSdx/Renderers/DX9/GSRendererDX9.cpp index 68193e1364..427c72980e 100644 --- a/plugins/GSdx/Renderers/DX9/GSRendererDX9.cpp +++ b/plugins/GSdx/Renderers/DX9/GSRendererDX9.cpp @@ -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) diff --git a/plugins/GSdx/Renderers/DX9/GSRendererDX9.h b/plugins/GSdx/Renderers/DX9/GSRendererDX9.h index 970be1e195..ed614569d6 100644 --- a/plugins/GSdx/Renderers/DX9/GSRendererDX9.h +++ b/plugins/GSdx/Renderers/DX9/GSRendererDX9.h @@ -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); diff --git a/plugins/GSdx/Renderers/DXCommon/GSRendererDX.cpp b/plugins/GSdx/Renderers/DXCommon/GSRendererDX.cpp index 988c9c407d..68e5d8bd24 100644 --- a/plugins/GSdx/Renderers/DXCommon/GSRendererDX.cpp +++ b/plugins/GSdx/Renderers/DXCommon/GSRendererDX.cpp @@ -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); diff --git a/plugins/GSdx/Renderers/DXCommon/GSRendererDX.h b/plugins/GSdx/Renderers/DXCommon/GSRendererDX.h index 30f76dc942..5dd44c5a3f 100644 --- a/plugins/GSdx/Renderers/DXCommon/GSRendererDX.h +++ b/plugins/GSdx/Renderers/DXCommon/GSRendererDX.h @@ -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) {}