mirror of https://github.com/PCSX2/pcsx2.git
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.
This commit is contained in:
parent
2ad5db2279
commit
0e83b89dbd
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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) {}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -34,6 +34,7 @@ protected:
|
|||
Direct3DBlendState9 bs;
|
||||
} m_fba;
|
||||
|
||||
void EmulateTextureShuffleAndFbmask();
|
||||
void SetupIA(const float& sx, const float& sy);
|
||||
void UpdateFBA(GSTexture* rt);
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue