From 0e83b89dbddee367bdf9ee42cbce779a2f60ea98 Mon Sep 17 00:00:00 2001 From: lightningterror Date: Sun, 18 Mar 2018 10:08:36 +0100 Subject: [PATCH] GSdx-D3D: Texture and channel shuffle improvements. Texture Shuffle changes: Always Enable Texture shuffle on D3D10/11. Previously Texture shuffle was enabled if CRC hack level was below Full, this was kinda not good since D3D also relies on CRC hacks on Full so you could either stick with texture shuffle or crc hacks. Texture shuffle is not supported on D3D9, however we can do a partial port where instead of vertical lines with the effect we get the effect on the entire screen. Better than nothing I suppose. Ported some of the code from OpenGL to D3D ( just a copy - paste job :) ), part of the code misses a dedicated shader but we can still use it to fix various issues on many games. List of affected games tested so far: The Godfather, Final Fight Streetwise, The Suffering Ties that Bind, Urban Chaos have their vertical lines issues fixed (highly possible for other games as well), MGS and Stolen see an improvement but they are still broken without crc hacks. Other games that suffered similar issues are probably affected as well. Channel Shuffle changes: Update Channel Shuffle detection. A lot of games should see an improvement, MGS, Urban Chaos, Stolen have their top left corner issues resolved. Other games should be affected as well that use similar logic. They still miss a shader so some effects are still broken/show glitches but it's a nice improvement for D3D users. Shared changes: Texture Shuffle and Channel shuffle have been moved to their own dedicated functions. Should make things a bit cleaner. Move part of the code for Texture Shuffle to GSRendererHW to be shared across all HW renderers, should aboid copy paste/duplicate code. --- plugins/GSdx/GSRendererDX.cpp | 159 ++++++++++---------------------- plugins/GSdx/GSRendererDX.h | 2 + plugins/GSdx/GSRendererDX11.cpp | 115 +++++++++++++++++++++++ plugins/GSdx/GSRendererDX11.h | 1 + plugins/GSdx/GSRendererDX9.cpp | 13 +++ plugins/GSdx/GSRendererDX9.h | 1 + plugins/GSdx/GSRendererHW.cpp | 16 ++++ plugins/GSdx/GSRendererOGL.cpp | 15 --- 8 files changed, 197 insertions(+), 125 deletions(-) diff --git a/plugins/GSdx/GSRendererDX.cpp b/plugins/GSdx/GSRendererDX.cpp index acf283bb78..ecf5abafbf 100644 --- a/plugins/GSdx/GSRendererDX.cpp +++ b/plugins/GSdx/GSRendererDX.cpp @@ -147,6 +147,53 @@ void GSRendererDX::EmulateZbuffer() } } +void GSRendererDX::EmulateChannelShuffle(GSTexture** rt, 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]; @@ -302,32 +349,7 @@ void GSRendererDX::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc dev = (GSDeviceDX*)m_dev; - // 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; - - 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 (m_context->CLAMP.WMS == 3 && ((m_context->CLAMP.MAXU & 0x8) == 8)) { - ; - } else if (m_context->CLAMP.WMS == 3 && ((m_context->CLAMP.MINU & 0x8) == 0)) { - ; - } else { - m_channel_shuffle = false; - } - } + EmulateChannelShuffle(&rt, tex); // Upscaling hack to avoid various line/grid issues MergeSprite(tex); @@ -452,90 +474,7 @@ void GSRendererDX::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc m_ps_sel.key = 0; m_ps_ssel.key = 0; - // Gregory: code is not yet ready so let's only enable it when - // CRC is below the FULL level - if (m_texture_shuffle && (m_crc_hack_level < CRCHackLevel::Full)) { - m_ps_sel.shuffle = 1; - m_ps_sel.fmt = 0; - - const GIFRegXYOFFSET& o = m_context->XYOFFSET; - GSVertex* v = &m_vertex.buff[0]; - size_t count = m_vertex.next; - - // vertex position is 8 to 16 pixels, therefore it is the 16-31 bits of the colors - int pos = (v[0].XYZ.X - o.OFX) & 0xFF; - bool write_ba = (pos > 112 && pos < 136); - // Read texture is 8 to 16 pixels (same as above) - int tex_pos = v[0].U & 0xFF; - m_ps_sel.read_ba = (tex_pos > 112 && tex_pos < 144); - - GL_INS("Color shuffle %s => %s", m_ps_sel.read_ba ? "BA" : "RG", write_ba ? "BA" : "RG"); - - // Convert the vertex info to a 32 bits color format equivalent - for (size_t i = 0; i < count; i += 2) { - if (write_ba) - v[i].XYZ.X -= 128u; - else - v[i + 1].XYZ.X += 128u; - - if (m_ps_sel.read_ba) - v[i].U -= 128u; - else - v[i + 1].U += 128u; - - // Height is too big (2x). - int tex_offset = v[i].V & 0xF; - GSVector4i offset(o.OFY, tex_offset, o.OFY, tex_offset); - - GSVector4i tmp(v[i].XYZ.Y, v[i].V, v[i + 1].XYZ.Y, v[i + 1].V); - tmp = GSVector4i(tmp - offset).srl32(1) + offset; - - v[i].XYZ.Y = (uint16)tmp.x; - v[i].V = (uint16)tmp.y; - v[i + 1].XYZ.Y = (uint16)tmp.z; - v[i + 1].V = (uint16)tmp.w; - } - - // Please bang my head against the wall! - // 1/ Reduce the frame mask to a 16 bit format - const uint32& m = m_context->FRAME.FBMSK; - uint32 fbmask = ((m >> 3) & 0x1F) | ((m >> 6) & 0x3E0) | ((m >> 9) & 0x7C00) | ((m >> 16) & 0x8000); - om_bsel.wrgba = 0; - - // 2 Select the new mask (Please someone put SSE here) - if ((fbmask & 0xFF) == 0) { - if (write_ba) - om_bsel.wb = 1; - else - om_bsel.wr = 1; - } - else if ((fbmask & 0xFF) != 0xFF) { -#ifdef _DEBUG - fprintf(stderr, "Please fix me! wb %u wr %u\n", om_bsel.wb, om_bsel.wr); -#endif - //ASSERT(0); - } - - fbmask >>= 8; - if ((fbmask & 0xFF) == 0) { - if (write_ba) - om_bsel.wa = 1; - else - om_bsel.wg = 1; - } - else if ((fbmask & 0xFF) != 0xFF) { -#ifdef _DEBUG - fprintf(stderr, "Please fix me! wa %u wg %u\n", om_bsel.wa, om_bsel.wg); -#endif - //ASSERT(0); - } - - } - else { - //ps_sel.fmt = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmt; - - om_bsel.wrgba = ~GSVector4i::load((int)m_context->FRAME.FBMSK).eq8(GSVector4i::xffffffff()).mask(); - } + EmulateTextureShuffleAndFbmask(); if(DATE) { diff --git a/plugins/GSdx/GSRendererDX.h b/plugins/GSdx/GSRendererDX.h index e695ae3d00..bccab5b9e3 100644 --- a/plugins/GSdx/GSRendererDX.h +++ b/plugins/GSdx/GSRendererDX.h @@ -36,8 +36,10 @@ class GSRendererDX : public GSRendererHW protected: void EmulateAtst(const int pass, const GSTextureCache::Source* tex); void EmulateZbuffer(); + void EmulateChannelShuffle(GSTexture** rt, 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 SetupIA(const float& sx, const float& sy) = 0; virtual void UpdateFBA(GSTexture* rt) {} diff --git a/plugins/GSdx/GSRendererDX11.cpp b/plugins/GSdx/GSRendererDX11.cpp index b534df8ca4..574fff2d26 100644 --- a/plugins/GSdx/GSRendererDX11.cpp +++ b/plugins/GSdx/GSRendererDX11.cpp @@ -42,6 +42,121 @@ bool GSRendererDX11::CreateDevice(GSDevice* dev) return true; } +void GSRendererDX11::EmulateTextureShuffleAndFbmask() +{ + size_t count = m_vertex.next; + GSVertex* v = &m_vertex.buff[0]; + + // Note: D3D1011 is limited and can't read the current framebuffer so we can't have PS_FBMASK and PS_WRITE_RG shaders ported and working. + if (m_texture_shuffle) { + m_ps_sel.shuffle = 1; + m_ps_sel.fmt = 0; + + const GIFRegXYOFFSET& o = m_context->XYOFFSET; + + // vertex position is 8 to 16 pixels, therefore it is the 16-31 bits of the colors + int pos = (v[0].XYZ.X - o.OFX) & 0xFF; + bool write_ba = (pos > 112 && pos < 136); + // Read texture is 8 to 16 pixels (same as above) + float tw = (float)(1u << m_context->TEX0.TW); + int tex_pos = (PRIM->FST) ? v[0].U : (int)(tw * v[0].ST.S); + tex_pos &= 0xFF; + m_ps_sel.read_ba = (tex_pos > 112 && tex_pos < 144); + + // Convert the vertex info to a 32 bits color format equivalent + if (PRIM->FST) { + + for(size_t i = 0; i < count; i += 2) { + if (write_ba) + v[i].XYZ.X -= 128u; + else + v[i+1].XYZ.X += 128u; + + if (m_ps_sel.read_ba) + v[i].U -= 128u; + else + v[i+1].U += 128u; + + // Height is too big (2x). + int tex_offset = v[i].V & 0xF; + GSVector4i offset(o.OFY, tex_offset, o.OFY, tex_offset); + + GSVector4i tmp(v[i].XYZ.Y, v[i].V, v[i+1].XYZ.Y, v[i+1].V); + tmp = GSVector4i(tmp - offset).srl32(1) + offset; + + v[i].XYZ.Y = (uint16)tmp.x; + v[i].V = (uint16)tmp.y; + v[i+1].XYZ.Y = (uint16)tmp.z; + v[i+1].V = (uint16)tmp.w; + } + } else { + const float offset_8pix = 8.0f / tw; + + for(size_t i = 0; i < count; i += 2) { + if (write_ba) + v[i].XYZ.X -= 128u; + else + v[i+1].XYZ.X += 128u; + + if (m_ps_sel.read_ba) + v[i].ST.S -= offset_8pix; + else + v[i+1].ST.S += offset_8pix; + + // Height is too big (2x). + GSVector4i offset(o.OFY, o.OFY); + + GSVector4i tmp(v[i].XYZ.Y, v[i+1].XYZ.Y); + tmp = GSVector4i(tmp - offset).srl32(1) + offset; + + //fprintf(stderr, "Before %d, After %d\n", v[i+1].XYZ.Y, tmp.y); + v[i].XYZ.Y = (uint16)tmp.x; + v[i].ST.T /= 2.0f; + v[i+1].XYZ.Y = (uint16)tmp.y; + v[i+1].ST.T /= 2.0f; + } + } + + // Please bang my head against the wall! + // 1/ Reduce the frame mask to a 16 bit format + // FIXME GSVector will be nice here + const uint32& m = m_context->FRAME.FBMSK; + uint32 fbmask = ((m >> 3) & 0x1F) | ((m >> 6) & 0x3E0) | ((m >> 9) & 0x7C00) | ((m >> 16) & 0x8000); + om_bsel.wrgba = 0; + + // 2 Select the new mask (Please someone put SSE here) + if ((fbmask & 0xFF) == 0) { + if (write_ba) + om_bsel.wb = 1; + else + om_bsel.wr = 1; + } else if ((fbmask & 0xFF) != 0xFF) { +#ifdef _DEBUG + fprintf(stderr, "Please fix me! wb %u wr %u\n", om_bsel.wb, om_bsel.wr); +#endif + //ASSERT(0); + } + + fbmask >>= 8; + if ((fbmask & 0xFF) == 0) { + if (write_ba) + om_bsel.wa = 1; + else + om_bsel.wg = 1; + } else if ((fbmask & 0xFF) != 0xFF) { +#ifdef _DEBUG + fprintf(stderr, "Please fix me! wa %u wg %u\n", om_bsel.wa, om_bsel.wg); +#endif + //ASSERT(0); + } + + } else { + //m_ps_sel.fmt = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmt; + + om_bsel.wrgba = ~GSVector4i::load((int)m_context->FRAME.FBMSK).eq8(GSVector4i::xffffffff()).mask(); + } +} + void GSRendererDX11::SetupIA(const float& sx, const float& sy) { GSDevice11* dev = (GSDevice11*)m_dev; diff --git a/plugins/GSdx/GSRendererDX11.h b/plugins/GSdx/GSRendererDX11.h index d3d27d2bc0..e24e1d53b3 100644 --- a/plugins/GSdx/GSRendererDX11.h +++ b/plugins/GSdx/GSRendererDX11.h @@ -30,6 +30,7 @@ class GSRendererDX11 : public GSRendererDX bool UserHacks_unscale_pt_ln; protected: + void EmulateTextureShuffleAndFbmask(); void SetupIA(const float& sx, const float& sy); public: diff --git a/plugins/GSdx/GSRendererDX9.cpp b/plugins/GSdx/GSRendererDX9.cpp index 761f447bda..bbbcc4408e 100644 --- a/plugins/GSdx/GSRendererDX9.cpp +++ b/plugins/GSdx/GSRendererDX9.cpp @@ -56,6 +56,19 @@ bool GSRendererDX9::CreateDevice(GSDevice* dev) return true; } +void GSRendererDX9::EmulateTextureShuffleAndFbmask() +{ + if (m_texture_shuffle) { + // We can do a partial port for D3D9 that skips the draw call to give it a slight improvement. + // It's still broken but more bearable. Broken effect is on the screen but fully instead of vertical lines. + throw GSDXRecoverableError(); + } else { + //m_ps_sel.fmt = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmt; + + om_bsel.wrgba = ~GSVector4i::load((int)m_context->FRAME.FBMSK).eq8(GSVector4i::xffffffff()).mask(); + } +} + void GSRendererDX9::SetupIA(const float& sx, const float& sy) { D3DPRIMITIVETYPE topology; diff --git a/plugins/GSdx/GSRendererDX9.h b/plugins/GSdx/GSRendererDX9.h index 817abed5cd..f89945449a 100644 --- a/plugins/GSdx/GSRendererDX9.h +++ b/plugins/GSdx/GSRendererDX9.h @@ -34,6 +34,7 @@ protected: Direct3DBlendState9 bs; } m_fba; + void EmulateTextureShuffleAndFbmask(); void SetupIA(const float& sx, const float& sy); void UpdateFBA(GSTexture* rt); diff --git a/plugins/GSdx/GSRendererHW.cpp b/plugins/GSdx/GSRendererHW.cpp index c2d30fde87..f68f054683 100644 --- a/plugins/GSdx/GSRendererHW.cpp +++ b/plugins/GSdx/GSRendererHW.cpp @@ -882,6 +882,22 @@ void GSRendererHW::Draw() m_texture_shuffle = (GSLocalMemory::m_psm[context->FRAME.PSM].bpp == 16) && (tex_psm.bpp == 16) && draw_sprite_tex && tex->m_32_bits_fmt; + // Shadow_of_memories_Shadow_Flickering (Okami mustn't call this code) + if (m_texture_shuffle && m_vertex.next < 3 && PRIM->FST && (m_context->FRAME.FBMSK == 0)) { + // Avious dubious call to m_texture_shuffle on 16 bits games + // The pattern is severals column of 8 pixels. A single sprite + // smell fishy but a big sprite is wrong. + + // Tomb Raider Angel of Darkness relies on this behavior to produce a fog effect. + // In this case, the address of the framebuffer and texture are the same. + // The game will take RG => BA and then the BA => RG of next pixels. + // However, only RG => BA needs to be emulated because RG isn't used. + GL_INS("WARNING: Possible misdetection of a texture shuffle effect"); + + GSVertex* v = &m_vertex.buff[0]; + m_texture_shuffle = ((v[1].U - v[0].U) < 256) || m_context->FRAME.Block() == m_context->TEX0.TBP0; + } + // Texture shuffle is not yet supported with strange clamp mode ASSERT(!m_texture_shuffle || (context->CLAMP.WMS < 3 && context->CLAMP.WMT < 3)); diff --git a/plugins/GSdx/GSRendererOGL.cpp b/plugins/GSdx/GSRendererOGL.cpp index 47033ae565..f7ed7c6227 100644 --- a/plugins/GSdx/GSRendererOGL.cpp +++ b/plugins/GSdx/GSRendererOGL.cpp @@ -229,21 +229,6 @@ void GSRendererOGL::EmulateTextureShuffleAndFbmask() size_t count = m_vertex.next; GSVertex* v = &m_vertex.buff[0]; - // Shadow_of_memories_Shadow_Flickering (Okami mustn't call this code) - if (m_texture_shuffle && count < 3 && PRIM->FST && (m_context->FRAME.FBMSK == 0)) { - // Avious dubious call to m_texture_shuffle on 16 bits games - // The pattern is severals column of 8 pixels. A single sprite - // smell fishy but a big sprite is wrong. - - // Tomb Raider Angel of Darkness relies on this behavior to produce a fog effect. - // In this case, the address of the framebuffer and texture are the same. - // The game will take RG => BA and then the BA => RG of next pixels. - // However, only RG => BA needs to be emulated because RG isn't used. - GL_INS("WARNING: Possible misdetection of a texture shuffle effect"); - m_texture_shuffle = ((v[1].U - v[0].U) < 256) || m_context->FRAME.Block() == m_context->TEX0.TBP0; - } - - if (m_texture_shuffle) { m_ps_sel.shuffle = 1; m_ps_sel.dfmt = 0;