mirror of https://github.com/PCSX2/pcsx2.git
Compare commits
16 Commits
56f89f5f0a
...
42aba2e0db
Author | SHA1 | Date |
---|---|---|
refractionpcsx2 | 42aba2e0db | |
refractionpcsx2 | afc0c2581d | |
refractionpcsx2 | f015b14e50 | |
refractionpcsx2 | fd53ce2ec6 | |
refractionpcsx2 | 1ac2d9fba3 | |
refractionpcsx2 | 80e50b87f7 | |
refractionpcsx2 | 4d37a9721f | |
refractionpcsx2 | 8de5f53252 | |
refractionpcsx2 | e9341bde7c | |
refractionpcsx2 | da3e5c0aad | |
refractionpcsx2 | 7b9c5b7543 | |
refractionpcsx2 | 9ddb4980eb | |
refractionpcsx2 | 99f7db509e | |
refractionpcsx2 | 3b17ccac36 | |
refractionpcsx2 | 8865ad90ff | |
refractionpcsx2 | 9d6e500035 |
|
@ -1967,6 +1967,7 @@ SCAJ-20125:
|
||||||
clampModes:
|
clampModes:
|
||||||
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
|
textureInsideRT: 1 # Fixes heat haze half screen problem.
|
||||||
alignSprite: 1
|
alignSprite: 1
|
||||||
halfPixelOffset: 4 # Align post.
|
halfPixelOffset: 4 # Align post.
|
||||||
nativeScaling: 1 # Fixes depth of field effect.
|
nativeScaling: 1 # Fixes depth of field effect.
|
||||||
|
@ -1977,6 +1978,7 @@ SCAJ-20126:
|
||||||
clampModes:
|
clampModes:
|
||||||
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
|
textureInsideRT: 1 # Fixes heat haze half screen problem.
|
||||||
alignSprite: 1
|
alignSprite: 1
|
||||||
halfPixelOffset: 4 # Align post.
|
halfPixelOffset: 4 # Align post.
|
||||||
nativeScaling: 1 # Fixes depth of field effect.
|
nativeScaling: 1 # Fixes depth of field effect.
|
||||||
|
@ -2454,6 +2456,7 @@ SCAJ-20199:
|
||||||
clampModes:
|
clampModes:
|
||||||
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
|
textureInsideRT: 1 # Fixes heat haze half screen problem.
|
||||||
alignSprite: 1
|
alignSprite: 1
|
||||||
halfPixelOffset: 4 # Align post.
|
halfPixelOffset: 4 # Align post.
|
||||||
nativeScaling: 1 # Fixes depth of field effect.
|
nativeScaling: 1 # Fixes depth of field effect.
|
||||||
|
@ -4149,6 +4152,7 @@ SCED-53538:
|
||||||
clampModes:
|
clampModes:
|
||||||
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
|
textureInsideRT: 1 # Fixes heat haze half screen problem.
|
||||||
alignSprite: 1
|
alignSprite: 1
|
||||||
halfPixelOffset: 4 # Align post.
|
halfPixelOffset: 4 # Align post.
|
||||||
nativeScaling: 1 # Fixes depth of field effect.
|
nativeScaling: 1 # Fixes depth of field effect.
|
||||||
|
@ -5769,6 +5773,7 @@ SCES-53202:
|
||||||
clampModes:
|
clampModes:
|
||||||
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
|
textureInsideRT: 1 # Fixes heat haze half screen problem.
|
||||||
alignSprite: 1
|
alignSprite: 1
|
||||||
halfPixelOffset: 4 # Align post.
|
halfPixelOffset: 4 # Align post.
|
||||||
nativeScaling: 1 # Fixes depth of field effect.
|
nativeScaling: 1 # Fixes depth of field effect.
|
||||||
|
@ -7212,6 +7217,7 @@ SCKA-20049:
|
||||||
clampModes:
|
clampModes:
|
||||||
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
|
textureInsideRT: 1 # Fixes heat haze half screen problem.
|
||||||
alignSprite: 1
|
alignSprite: 1
|
||||||
halfPixelOffset: 4 # Align post.
|
halfPixelOffset: 4 # Align post.
|
||||||
nativeScaling: 1 # Fixes depth of field effect.
|
nativeScaling: 1 # Fixes depth of field effect.
|
||||||
|
@ -7435,6 +7441,7 @@ SCKA-20081:
|
||||||
clampModes:
|
clampModes:
|
||||||
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
|
textureInsideRT: 1 # Fixes heat haze half screen problem.
|
||||||
alignSprite: 1
|
alignSprite: 1
|
||||||
halfPixelOffset: 4 # Align post.
|
halfPixelOffset: 4 # Align post.
|
||||||
nativeScaling: 1 # Fixes depth of field effect.
|
nativeScaling: 1 # Fixes depth of field effect.
|
||||||
|
@ -57353,6 +57360,7 @@ SLPS-25510:
|
||||||
clampModes:
|
clampModes:
|
||||||
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
|
textureInsideRT: 1 # Fixes heat haze half screen problem.
|
||||||
alignSprite: 1 # Fixes vertical lines.
|
alignSprite: 1 # Fixes vertical lines.
|
||||||
halfPixelOffset: 4 # Align post.
|
halfPixelOffset: 4 # Align post.
|
||||||
nativeScaling: 1 # Fixes depth of field effect.
|
nativeScaling: 1 # Fixes depth of field effect.
|
||||||
|
@ -60518,6 +60526,7 @@ SLPS-73223:
|
||||||
clampModes:
|
clampModes:
|
||||||
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
|
textureInsideRT: 1 # Fixes heat haze half screen problem.
|
||||||
alignSprite: 1 # Fixes vertical lines.
|
alignSprite: 1 # Fixes vertical lines.
|
||||||
halfPixelOffset: 4 # Align post.
|
halfPixelOffset: 4 # Align post.
|
||||||
nativeScaling: 1 # Fixes depth of field effect.
|
nativeScaling: 1 # Fixes depth of field effect.
|
||||||
|
@ -66516,6 +66525,7 @@ SLUS-21059:
|
||||||
clampModes:
|
clampModes:
|
||||||
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
|
textureInsideRT: 1 # Fixes heat haze half screen problem.
|
||||||
alignSprite: 1 # Fixes vertical lines.
|
alignSprite: 1 # Fixes vertical lines.
|
||||||
halfPixelOffset: 4 # Align post.
|
halfPixelOffset: 4 # Align post.
|
||||||
nativeScaling: 1 # Fixes depth of field effect.
|
nativeScaling: 1 # Fixes depth of field effect.
|
||||||
|
@ -67060,6 +67070,7 @@ SLUS-21160:
|
||||||
clampModes:
|
clampModes:
|
||||||
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
eeClampMode: 2 # Fixes camera and stops constant coin noises on Pirates Cove.
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
|
textureInsideRT: 1 # Fixes heat haze half screen problem.
|
||||||
alignSprite: 1 # Fixes vertical lines.
|
alignSprite: 1 # Fixes vertical lines.
|
||||||
halfPixelOffset: 4 # Align post.
|
halfPixelOffset: 4 # Align post.
|
||||||
nativeScaling: 1 # Fixes depth of field effect.
|
nativeScaling: 1 # Fixes depth of field effect.
|
||||||
|
|
|
@ -1123,11 +1123,8 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||||
{
|
{
|
||||||
if (PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
|
if (PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
|
||||||
{
|
{
|
||||||
C.rb = C.br;
|
C.br = C.rb;
|
||||||
float g_temp = C.g;
|
C.ag = C.ga;
|
||||||
|
|
||||||
C.g = C.a;
|
|
||||||
C.a = g_temp;
|
|
||||||
}
|
}
|
||||||
else if(PS_PROCESS_BA & SHUFFLE_READ)
|
else if(PS_PROCESS_BA & SHUFFLE_READ)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1086,11 +1086,8 @@ void ps_main()
|
||||||
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u)));
|
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u)));
|
||||||
#elif PS_SHUFFLE_ACROSS
|
#elif PS_SHUFFLE_ACROSS
|
||||||
#if(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
|
#if(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
|
||||||
C.rb = C.br;
|
C.br = C.rb;
|
||||||
float g_temp = C.g;
|
C.ag = C.ga;
|
||||||
|
|
||||||
C.g = C.a;
|
|
||||||
C.a = g_temp;
|
|
||||||
#elif(PS_PROCESS_BA & SHUFFLE_READ)
|
#elif(PS_PROCESS_BA & SHUFFLE_READ)
|
||||||
C.rb = C.bb;
|
C.rb = C.bb;
|
||||||
C.ga = C.aa;
|
C.ga = C.aa;
|
||||||
|
|
|
@ -945,7 +945,7 @@ vec4 ps_color()
|
||||||
vec4 T = sample_color(st);
|
vec4 T = sample_color(st);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if PS_SHUFFLE && !PS_READ16_SRC && !PS_SHUFFLE_SAME
|
#if PS_SHUFFLE && !PS_READ16_SRC && !PS_SHUFFLE_SAME && !(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
|
||||||
uvec4 denorm_c_before = uvec4(T);
|
uvec4 denorm_c_before = uvec4(T);
|
||||||
#if (PS_PROCESS_BA & SHUFFLE_READ)
|
#if (PS_PROCESS_BA & SHUFFLE_READ)
|
||||||
T.r = float((denorm_c_before.b << 3) & 0xF8u);
|
T.r = float((denorm_c_before.b << 3) & 0xF8u);
|
||||||
|
@ -1320,7 +1320,7 @@ void main()
|
||||||
ps_blend(C, alpha_blend);
|
ps_blend(C, alpha_blend);
|
||||||
|
|
||||||
#if PS_SHUFFLE
|
#if PS_SHUFFLE
|
||||||
#if !PS_READ16_SRC && !PS_SHUFFLE_SAME
|
#if !PS_READ16_SRC && !PS_SHUFFLE_SAME && !(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
|
||||||
uvec4 denorm_c_after = uvec4(C);
|
uvec4 denorm_c_after = uvec4(C);
|
||||||
#if (PS_PROCESS_BA & SHUFFLE_READ)
|
#if (PS_PROCESS_BA & SHUFFLE_READ)
|
||||||
C.b = float(((denorm_c_after.r >> 3) & 0x1Fu) | ((denorm_c_after.g << 2) & 0xE0u));
|
C.b = float(((denorm_c_after.r >> 3) & 0x1Fu) | ((denorm_c_after.g << 2) & 0xE0u));
|
||||||
|
@ -1350,11 +1350,8 @@ void main()
|
||||||
// Write RB part. Mask will take care of the correct destination
|
// Write RB part. Mask will take care of the correct destination
|
||||||
#elif PS_SHUFFLE_ACROSS
|
#elif PS_SHUFFLE_ACROSS
|
||||||
#if(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
|
#if(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
|
||||||
C.rb = C.br;
|
C.br = C.rb;
|
||||||
float g_temp = C.g;
|
C.ag = C.ga;
|
||||||
|
|
||||||
C.g = C.a;
|
|
||||||
C.a = g_temp;
|
|
||||||
#elif(PS_PROCESS_BA & SHUFFLE_READ)
|
#elif(PS_PROCESS_BA & SHUFFLE_READ)
|
||||||
C.rb = C.bb;
|
C.rb = C.bb;
|
||||||
C.ga = C.aa;
|
C.ga = C.aa;
|
||||||
|
|
|
@ -467,7 +467,8 @@ void GSState::DumpVertices(const std::string& filename)
|
||||||
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.R) << DEL;
|
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.R) << DEL;
|
||||||
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.G) << DEL;
|
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.G) << DEL;
|
||||||
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.B) << DEL;
|
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.B) << DEL;
|
||||||
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.A);
|
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.A) << DEL;
|
||||||
|
file << "FOG: " << std::setfill('0') << std::setw(3) << unsigned(v.FOG);
|
||||||
file << std::endl;
|
file << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1674,7 +1675,8 @@ void GSState::FlushPrim()
|
||||||
Console.Warning("GS: Possible invalid draw, Frame PSM %x ZPSM %x", m_context->FRAME.PSM, m_context->ZBUF.PSM);
|
Console.Warning("GS: Possible invalid draw, Frame PSM %x ZPSM %x", m_context->FRAME.PSM, m_context->ZBUF.PSM);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
// Update scissor, it may have been modified by a previous draw
|
||||||
|
m_env.CTXT[PRIM->CTXT].UpdateScissor();
|
||||||
m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM));
|
m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM));
|
||||||
|
|
||||||
// Texel coordinate rounding
|
// Texel coordinate rounding
|
||||||
|
@ -3094,6 +3096,16 @@ __forceinline bool GSState::IsAutoFlushDraw(u32 prim)
|
||||||
if (!(GSUtil::GetChannelMask(m_context->TEX0.PSM) & GSUtil::GetChannelMask(m_context->FRAME.PSM, m_context->FRAME.FBMSK | ~(GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk))))
|
if (!(GSUtil::GetChannelMask(m_context->TEX0.PSM) & GSUtil::GetChannelMask(m_context->FRAME.PSM, m_context->FRAME.FBMSK | ~(GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk))))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// Try to detect shuffles, because these will not autoflush, they by design clash.
|
||||||
|
if (GSLocalMemory::m_psm[m_context->FRAME.PSM].bpp == 16 && GSLocalMemory::m_psm[m_context->TEX0.PSM].bpp == 16)
|
||||||
|
{
|
||||||
|
// Pretty confident here...
|
||||||
|
GSVertex* buffer = &m_vertex.buff[0];
|
||||||
|
const bool const_spacing = std::abs(buffer[m_index.buff[0]].U - buffer[m_index.buff[0]].XYZ.X) == std::abs(m_v.U - m_v.XYZ.X) && std::abs(buffer[m_index.buff[1]].XYZ.X - buffer[m_index.buff[0]].XYZ.X) < 64;
|
||||||
|
|
||||||
|
if (const_spacing)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const u32 frame_mask = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk;
|
const u32 frame_mask = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk;
|
||||||
const bool frame_hit = m_context->FRAME.Block() == m_context->TEX0.TBP0 && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL == 2) && ((m_context->FRAME.FBMSK & frame_mask) != frame_mask);
|
const bool frame_hit = m_context->FRAME.Block() == m_context->TEX0.TBP0 && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL == 2) && ((m_context->FRAME.FBMSK & frame_mask) != frame_mask);
|
||||||
// There's a strange behaviour we need to test on a PS2 here, if the FRAME is a Z format, like Powerdrome something swaps over, and it seems Alpha Fail of "FB Only" writes to the Z.. it's odd.
|
// There's a strange behaviour we need to test on a PS2 here, if the FRAME is a Z format, like Powerdrome something swaps over, and it seems Alpha Fail of "FB Only" writes to the Z.. it's odd.
|
||||||
|
@ -3859,7 +3871,8 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCL
|
||||||
const GSVector2 grad(uv_range / pos_range);
|
const GSVector2 grad(uv_range / pos_range);
|
||||||
// Adjust texture range when sprites get scissor clipped. Since we linearly interpolate, this
|
// Adjust texture range when sprites get scissor clipped. Since we linearly interpolate, this
|
||||||
// optimization doesn't work when perspective correction is enabled.
|
// optimization doesn't work when perspective correction is enabled.
|
||||||
if (m_vt.m_primclass == GS_SPRITE_CLASS && PRIM->FST == 1 && m_primitive_covers_without_gaps != NoGapsType::GapsFound)
|
// Allowing for quads when the gradiant is 1. It's not guaranteed (would need to check the grandient on each vector), but should be close enough.
|
||||||
|
if ((m_vt.m_primclass == GS_SPRITE_CLASS || (m_vt.m_primclass == GS_TRIANGLE_CLASS && TrianglesAreQuads(false) && grad.x == 1.0f && grad.y == 1.0f)) && m_primitive_covers_without_gaps != NoGapsType::GapsFound)
|
||||||
{
|
{
|
||||||
// When coordinates are fractional, GS appears to draw to the right/bottom (effectively
|
// When coordinates are fractional, GS appears to draw to the right/bottom (effectively
|
||||||
// taking the ceiling), not to the top/left (taking the floor).
|
// taking the ceiling), not to the top/left (taking the floor).
|
||||||
|
@ -3870,11 +3883,24 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCL
|
||||||
|
|
||||||
const GSVertex* vert_first = &m_vertex.buff[m_index.buff[0]];
|
const GSVertex* vert_first = &m_vertex.buff[m_index.buff[0]];
|
||||||
const GSVertex* vert_second = &m_vertex.buff[m_index.buff[1]];
|
const GSVertex* vert_second = &m_vertex.buff[m_index.buff[1]];
|
||||||
|
const GSVertex* vert_third = &m_vertex.buff[m_index.buff[2]];
|
||||||
|
|
||||||
GSVector4 new_st = st;
|
GSVector4 new_st = st;
|
||||||
|
bool u_forward_check = false;
|
||||||
|
bool x_forward_check = false;
|
||||||
|
if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
|
||||||
|
{
|
||||||
|
u_forward_check = PRIM->FST ? ((vert_first->U < vert_second->U) || (vert_first->U < vert_third->U)) : (((vert_first->ST.S / vert_first->RGBAQ.Q) < (vert_second->ST.S / vert_second->RGBAQ.Q)) || ((vert_first->ST.S / vert_first->RGBAQ.Q) < (vert_third->ST.S / vert_third->RGBAQ.Q)));
|
||||||
|
x_forward_check = (vert_first->XYZ.X < vert_second->XYZ.X) || (vert_first->XYZ.X < vert_third->XYZ.X);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u_forward_check = PRIM->FST ? (vert_first->U < vert_second->U) : ((vert_first->ST.T / vert_first->RGBAQ.Q) < (vert_second->ST.T / vert_first->RGBAQ.Q));
|
||||||
|
x_forward_check = vert_first->XYZ.Y < vert_second->XYZ.Y;
|
||||||
|
}
|
||||||
// Check if the UV coords are going in a different direction to the verts, if they match direction, no need to swap
|
// Check if the UV coords are going in a different direction to the verts, if they match direction, no need to swap
|
||||||
const bool u_forward = vert_first->U < vert_second->U;
|
const bool u_forward = u_forward_check;
|
||||||
const bool x_forward = vert_first->XYZ.X < vert_second->XYZ.X;
|
const bool x_forward = x_forward_check;
|
||||||
const bool swap_x = u_forward != x_forward;
|
const bool swap_x = u_forward != x_forward;
|
||||||
|
|
||||||
if (int_rc.left < scissored_rc.left)
|
if (int_rc.left < scissored_rc.left)
|
||||||
|
@ -3897,9 +3923,20 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCL
|
||||||
st.x = new_st.x;
|
st.x = new_st.x;
|
||||||
st.z = new_st.z;
|
st.z = new_st.z;
|
||||||
}
|
}
|
||||||
|
bool v_forward_check = false;
|
||||||
const bool v_forward = vert_first->V < vert_second->V;
|
bool y_forward_check = false;
|
||||||
const bool y_forward = vert_first->XYZ.Y < vert_second->XYZ.Y;
|
if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
|
||||||
|
{
|
||||||
|
v_forward_check = PRIM->FST ? ((vert_first->V < vert_second->V) || (vert_first->V < vert_third->V)) : (((vert_first->ST.T / vert_first->RGBAQ.Q) < (vert_second->ST.T / vert_second->RGBAQ.Q)) || ((vert_first->ST.T / vert_first->RGBAQ.Q) < (vert_third->ST.T / vert_third->RGBAQ.Q)));
|
||||||
|
y_forward_check = (vert_first->XYZ.Y < vert_second->XYZ.Y) || (vert_first->XYZ.Y < vert_third->XYZ.Y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v_forward_check = PRIM->FST ? (vert_first->V < vert_second->V) : ((vert_first->ST.T / vert_first->RGBAQ.Q) < (vert_second->ST.T / vert_first->RGBAQ.Q));
|
||||||
|
y_forward_check = vert_first->XYZ.Y < vert_second->XYZ.Y;
|
||||||
|
}
|
||||||
|
const bool v_forward = v_forward_check;
|
||||||
|
const bool y_forward = y_forward_check;
|
||||||
const bool swap_y = v_forward != y_forward;
|
const bool swap_y = v_forward != y_forward;
|
||||||
|
|
||||||
if (int_rc.top < scissored_rc.top)
|
if (int_rc.top < scissored_rc.top)
|
||||||
|
|
|
@ -224,6 +224,8 @@ public:
|
||||||
bool m_texflush_flag = false;
|
bool m_texflush_flag = false;
|
||||||
bool m_isPackedUV_HackFlag = false;
|
bool m_isPackedUV_HackFlag = false;
|
||||||
bool m_channel_shuffle = false;
|
bool m_channel_shuffle = false;
|
||||||
|
bool m_in_target_draw = false;
|
||||||
|
u32 m_target_offset = 0;
|
||||||
u8 m_scanmask_used = 0;
|
u8 m_scanmask_used = 0;
|
||||||
u32 m_dirty_gs_regs = 0;
|
u32 m_dirty_gs_regs = 0;
|
||||||
int m_backed_up_ctx = 0;
|
int m_backed_up_ctx = 0;
|
||||||
|
|
|
@ -1599,6 +1599,11 @@ public:
|
||||||
return loadh(&v);
|
return loadh(&v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__forceinline static GSVector4i loadl(const GSVector2i& v)
|
||||||
|
{
|
||||||
|
return loadl(&v);
|
||||||
|
}
|
||||||
|
|
||||||
__forceinline static GSVector4i load(const void* pl, const void* ph)
|
__forceinline static GSVector4i load(const void* pl, const void* ph)
|
||||||
{
|
{
|
||||||
return loadh(ph, loadl(pl));
|
return loadh(ph, loadl(pl));
|
||||||
|
|
|
@ -194,7 +194,7 @@ bool GSHwHack::GSC_Tekken5(GSRendererHW& r, int& skip)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!s_nativeres && r.PRIM->PRIM == GS_SPRITE && RTME && RTEX0.TFX == 1 && RFPSM == RTPSM && RTPSM == PSMCT32 && RFBMSK == 0xFF000000 && r.m_index.tail > 2)
|
if (!s_nativeres && r.PRIM->PRIM == GS_SPRITE && RTME && RTEX0.TFX == 1 && !r.PRIM->ABE && RFPSM == RTPSM && RTPSM == PSMCT32 && RFBMSK == 0xFF000000 && r.m_index.tail > 2)
|
||||||
{
|
{
|
||||||
// Don't enable hack on native res.
|
// Don't enable hack on native res.
|
||||||
// Fixes ghosting/blur effect and white lines appearing in stages: Moonfit Wilderness, Acid Rain - caused by upscaling.
|
// Fixes ghosting/blur effect and white lines appearing in stages: Moonfit Wilderness, Acid Rain - caused by upscaling.
|
||||||
|
@ -204,12 +204,6 @@ bool GSHwHack::GSC_Tekken5(GSRendererHW& r, int& skip)
|
||||||
const GSVector4i read_size(r.m_vt.m_min.t.x, r.m_vt.m_min.t.y, r.m_vt.m_max.t.x + 0.5f, r.m_vt.m_max.t.y + 0.5f);
|
const GSVector4i read_size(r.m_vt.m_min.t.x, r.m_vt.m_min.t.y, r.m_vt.m_max.t.x + 0.5f, r.m_vt.m_max.t.y + 0.5f);
|
||||||
r.ReplaceVerticesWithSprite(draw_size, read_size, GSVector2i(read_size.width(), read_size.height()), draw_size);
|
r.ReplaceVerticesWithSprite(draw_size, read_size, GSVector2i(read_size.width(), read_size.height()), draw_size);
|
||||||
}
|
}
|
||||||
else if (RZTST == 1 && RTME && (RFBP == 0x02bc0 || RFBP == 0x02be0 || RFBP == 0x02d00 || RFBP == 0x03480 || RFBP == 0x034a0) && RFPSM == RTPSM && RTBP0 == 0x00000 && RTPSM == PSMCT32)
|
|
||||||
{
|
|
||||||
// The moving display effect(flames) is not emulated properly in the entire screen so let's remove the effect in the stage: Burning Temple. Related to half screen bottom issue.
|
|
||||||
// Fixes black lines in the stage: Burning Temple - caused by upscaling. Note the black lines can also be fixed with Merge Sprite hack.
|
|
||||||
skip = 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1047,7 +1041,7 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
|
||||||
// compute shadow in RG,
|
// compute shadow in RG,
|
||||||
// save result in alpha with a TS,
|
// save result in alpha with a TS,
|
||||||
// Restore RG channel that we previously copied to render shadows.
|
// Restore RG channel that we previously copied to render shadows.
|
||||||
|
// Important note: The game downsizes the target to half height, then later expands it back up to full size, that's why PCSX2 doesn't like it, we don't support that behaviour.
|
||||||
const GIFRegTEX0& Texture = RTEX0;
|
const GIFRegTEX0& Texture = RTEX0;
|
||||||
|
|
||||||
GIFRegTEX0 Frame = {};
|
GIFRegTEX0 Frame = {};
|
||||||
|
@ -1058,9 +1052,9 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
|
||||||
if ((!rt) || (!RPRIM->TME) || (GSLocalMemory::m_psm[Texture.PSM].bpp != 16) || (GSLocalMemory::m_psm[Frame.PSM].bpp != 16) || (Texture.TBP0 == Frame.TBP0) || (Frame.TBW != 16 && Texture.TBW != 16))
|
if ((!rt) || (!RPRIM->TME) || (GSLocalMemory::m_psm[Texture.PSM].bpp != 16) || (GSLocalMemory::m_psm[Frame.PSM].bpp != 16) || (Texture.TBP0 == Frame.TBP0) || (Frame.TBW != 16 && Texture.TBW != 16))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
GL_INS("OI_SonicUnleashed replace draw by a copy");
|
GL_INS("OI_SonicUnleashed replace draw by a copy draw %d", r.s_n);
|
||||||
|
|
||||||
GSTextureCache::Target* src = g_texture_cache->LookupTarget(Texture, GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget);
|
GSTextureCache::Target* src = g_texture_cache->LookupTarget(Texture, GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, false, true, true, GSVector4i::zero(), true);
|
||||||
|
|
||||||
if (!src)
|
if (!src)
|
||||||
return true;
|
return true;
|
||||||
|
@ -1086,6 +1080,7 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
|
||||||
const GSVector2i copy_size(std::min(rt_size.x, src_size.x), std::min(rt_size.y, src_size.y));
|
const GSVector2i copy_size(std::min(rt_size.x, src_size.x), std::min(rt_size.y, src_size.y));
|
||||||
|
|
||||||
const GSVector4 sRect(0.0f, 0.0f, static_cast<float>(copy_size.x) / static_cast<float>(src_size.x), static_cast<float>(copy_size.y) / static_cast<float>(src_size.y));
|
const GSVector4 sRect(0.0f, 0.0f, static_cast<float>(copy_size.x) / static_cast<float>(src_size.x), static_cast<float>(copy_size.y) / static_cast<float>(src_size.y));
|
||||||
|
// This is kind of a bodge because the game confuses everything since the source is really 16bit and it assumes it's really drawing 16bit on the copy back, resizing the target.
|
||||||
const GSVector4 dRect(0, 0, copy_size.x, copy_size.y);
|
const GSVector4 dRect(0, 0, copy_size.x, copy_size.y);
|
||||||
|
|
||||||
g_gs_device->StretchRect(src->m_texture, sRect, rt, dRect, true, true, true, false);
|
g_gs_device->StretchRect(src->m_texture, sRect, rt, dRect, true, true, true, false);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -113,12 +113,14 @@ private:
|
||||||
void SetTCOffset();
|
void SetTCOffset();
|
||||||
|
|
||||||
bool IsPossibleChannelShuffle() const;
|
bool IsPossibleChannelShuffle() const;
|
||||||
|
bool IsPageCopy() const;
|
||||||
bool NextDrawMatchesShuffle() const;
|
bool NextDrawMatchesShuffle() const;
|
||||||
bool IsSplitTextureShuffle(GSTextureCache::Target* rt);
|
bool IsSplitTextureShuffle(GSTextureCache::Target* rt);
|
||||||
GSVector4i GetSplitTextureShuffleDrawRect() const;
|
GSVector4i GetSplitTextureShuffleDrawRect() const;
|
||||||
u32 GetEffectiveTextureShuffleFbmsk() const;
|
u32 GetEffectiveTextureShuffleFbmsk() const;
|
||||||
|
|
||||||
static GSVector4i GetDrawRectForPages(u32 bw, u32 psm, u32 num_pages);
|
static GSVector4i GetDrawRectForPages(u32 bw, u32 psm, u32 num_pages);
|
||||||
|
bool IsSinglePageDraw() const;
|
||||||
bool TryToResolveSinglePageFramebuffer(GIFRegFRAME& FRAME, bool only_next_draw);
|
bool TryToResolveSinglePageFramebuffer(GIFRegFRAME& FRAME, bool only_next_draw);
|
||||||
|
|
||||||
bool IsSplitClearActive() const;
|
bool IsSplitClearActive() const;
|
||||||
|
@ -172,6 +174,7 @@ private:
|
||||||
|
|
||||||
u32 m_last_channel_shuffle_fbmsk = 0;
|
u32 m_last_channel_shuffle_fbmsk = 0;
|
||||||
u32 m_last_channel_shuffle_fbp = 0;
|
u32 m_last_channel_shuffle_fbp = 0;
|
||||||
|
u32 m_last_channel_shuffle_tbp = 0;
|
||||||
u32 m_last_channel_shuffle_end_block = 0;
|
u32 m_last_channel_shuffle_end_block = 0;
|
||||||
|
|
||||||
GIFRegFRAME m_split_clear_start = {};
|
GIFRegFRAME m_split_clear_start = {};
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
|
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -234,7 +235,7 @@ bool GSTextureCache::CanTranslate(u32 bp, u32 bw, u32 spsm, GSVector4i r, u32 db
|
||||||
// The page width matches.
|
// The page width matches.
|
||||||
// The rect width is less than the width of the destination texture and the height is less than or equal to 1 page high.
|
// The rect width is less than the width of the destination texture and the height is less than or equal to 1 page high.
|
||||||
// The rect width and height is equal to the page size and it covers the width of the incoming bw, so lines are sequential.
|
// The rect width and height is equal to the page size and it covers the width of the incoming bw, so lines are sequential.
|
||||||
const bool page_aligned_rect = masked_rect.eq(r);
|
const bool page_aligned_rect = masked_rect.xyxy().eq(r.xyxy());
|
||||||
const bool width_match = ((bw * 64) / src_page_size.x) == ((dbw * 64) / dst_page_size.x);
|
const bool width_match = ((bw * 64) / src_page_size.x) == ((dbw * 64) / dst_page_size.x);
|
||||||
const bool sequential_pages = page_aligned_rect && r.x == 0 && r.z == src_pixel_width;
|
const bool sequential_pages = page_aligned_rect && r.x == 0 && r.z == src_pixel_width;
|
||||||
const bool single_row = (((bw * 64) / src_page_size.x) <= ((dbw * 64) / dst_page_size.x)) && r.z <= src_pixel_width && r.w <= src_page_size.y;
|
const bool single_row = (((bw * 64) / src_page_size.x) <= ((dbw * 64) / dst_page_size.x)) && r.z <= src_pixel_width && r.w <= src_page_size.y;
|
||||||
|
@ -273,6 +274,15 @@ GSVector4i GSTextureCache::TranslateAlignedRectByPage(u32 tbp, u32 tebp, u32 tbw
|
||||||
const int inc_horizontal_offset = (page_offset % src_pgw) * src_page_size.x;
|
const int inc_horizontal_offset = (page_offset % src_pgw) * src_page_size.x;
|
||||||
in_rect = (in_rect + GSVector4i(0, inc_vertical_offset).xyxy()).max_i32(GSVector4i(0));
|
in_rect = (in_rect + GSVector4i(0, inc_vertical_offset).xyxy()).max_i32(GSVector4i(0));
|
||||||
in_rect = (in_rect + GSVector4i(inc_horizontal_offset, 0).xyxy()).max_i32(GSVector4i(0));
|
in_rect = (in_rect + GSVector4i(inc_horizontal_offset, 0).xyxy()).max_i32(GSVector4i(0));
|
||||||
|
|
||||||
|
// Project Snowblind and Tomb Raider access the rect offset by 1 page and use a region to correct it, we need to account for that here.
|
||||||
|
if (in_rect.x >= (src_pgw * src_page_size.x))
|
||||||
|
{
|
||||||
|
in_rect.z -= src_pgw * src_page_size.x;
|
||||||
|
in_rect.x -= src_pgw * src_page_size.x;
|
||||||
|
in_rect.y += src_page_size.y;
|
||||||
|
in_rect.w += src_page_size.y;
|
||||||
|
}
|
||||||
page_offset = 0;
|
page_offset = 0;
|
||||||
single_page = (in_rect.width() / src_page_size.x) <= 1 && (in_rect.height() / src_page_size.y) <= 1;
|
single_page = (in_rect.width() / src_page_size.x) <= 1 && (in_rect.height() / src_page_size.y) <= 1;
|
||||||
}
|
}
|
||||||
|
@ -1091,9 +1101,11 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||||
req_rect.y = region.HasY() ? region.GetMinY() : 0;
|
req_rect.y = region.HasY() ? region.GetMinY() : 0;
|
||||||
|
|
||||||
GSVector4i block_boundary_rect = req_rect;
|
GSVector4i block_boundary_rect = req_rect;
|
||||||
|
block_boundary_rect.x = block_boundary_rect.x & ~(psm_s.bs.x - 1);
|
||||||
|
block_boundary_rect.y = block_boundary_rect.y & ~(psm_s.bs.y - 1);
|
||||||
// Round up to the nearst block boundary for lookup to avoid problems due to bilinear and inclusive rects.
|
// Round up to the nearst block boundary for lookup to avoid problems due to bilinear and inclusive rects.
|
||||||
block_boundary_rect.z = std::max(req_rect.x + 1, (block_boundary_rect.z + (psm_s.bs.x - 2)) & ~(psm_s.bs.x - 1));
|
block_boundary_rect.z = std::max(req_rect.x + 1, (block_boundary_rect.z + (psm_s.bs.x / 2)) & ~(psm_s.bs.x - 1));
|
||||||
block_boundary_rect.w = std::max(req_rect.y + 1, (block_boundary_rect.w + (psm_s.bs.y - 2)) & ~(psm_s.bs.y - 1));
|
block_boundary_rect.w = std::max(req_rect.y + 1, (block_boundary_rect.w + (psm_s.bs.y / 2)) & ~(psm_s.bs.y - 1));
|
||||||
|
|
||||||
// Arc the Lad finds the wrong surface here when looking for a depth stencil.
|
// Arc the Lad finds the wrong surface here when looking for a depth stencil.
|
||||||
// Since we're currently not caching depth stencils (check ToDo in CreateSource) we should not look for it here.
|
// Since we're currently not caching depth stencils (check ToDo in CreateSource) we should not look for it here.
|
||||||
|
@ -1115,12 +1127,13 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||||
if (((bp & (BLOCKS_PER_PAGE - 1)) != (t->m_TEX0.TBP0 & (BLOCKS_PER_PAGE - 1))) && (bp & (BLOCKS_PER_PAGE - 1)))
|
if (((bp & (BLOCKS_PER_PAGE - 1)) != (t->m_TEX0.TBP0 & (BLOCKS_PER_PAGE - 1))) && (bp & (BLOCKS_PER_PAGE - 1)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
//const bool overlaps = t->Inside(bp, bw, psm, block_boundary_rect);
|
||||||
const bool overlaps = t->Overlaps(bp, bw, psm, block_boundary_rect);
|
const bool overlaps = t->Overlaps(bp, bw, psm, block_boundary_rect);
|
||||||
|
|
||||||
// Try to make sure the target has available what we need, be careful of self referencing frames with font in the alpha.
|
// Try to make sure the target has available what we need, be careful of self referencing frames with font in the alpha.
|
||||||
// Also is we have already found a target which we had to offset in to by using a region or exact address,
|
// Also is we have already found a target which we had to offset in to by using a region or exact address,
|
||||||
// it's probable that's more correct than being inside (Tomb Raider Legends + Project Snowblind)
|
// it's probable that's more correct than being inside (Tomb Raider Legends + Project Snowblind)
|
||||||
if (!overlaps || (found_t && dst->m_TEX0.TBP0 >= bp && (GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw)))
|
// Vakyrie Profile 2 also has some in draws which get done on a different target due to a slight offset, so we need to make sure we have the newer one.
|
||||||
|
if (!overlaps || (found_t && (GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const bool width_match = (std::max(64U, bw * 64U) >> GSLocalMemory::m_psm[psm].info.pageShiftX()) ==
|
const bool width_match = (std::max(64U, bw * 64U) >> GSLocalMemory::m_psm[psm].info.pageShiftX()) ==
|
||||||
|
@ -1360,6 +1373,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||||
DevCon.Warning("Failed to update dst matched texture");
|
DevCon.Warning("Failed to update dst matched texture");
|
||||||
}
|
}
|
||||||
t->m_valid_rgb = true;
|
t->m_valid_rgb = true;
|
||||||
|
t->m_TEX0 = dst_match->m_TEX0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1445,13 +1459,28 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||||
// Make sure the texture actually is INSIDE the RT, it's possibly not valid if it isn't.
|
// Make sure the texture actually is INSIDE the RT, it's possibly not valid if it isn't.
|
||||||
// Also check BP >= TBP, create source isn't equpped to expand it backwards and all data comes from the target. (GH3)
|
// Also check BP >= TBP, create source isn't equpped to expand it backwards and all data comes from the target. (GH3)
|
||||||
else if (GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets &&
|
else if (GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets &&
|
||||||
(GSLocalMemory::m_psm[color_psm].bpp >= 16 || (possible_shuffle && GSLocalMemory::m_psm[color_psm].bpp == 8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 32)) && // Channel shuffles or non indexed lookups.
|
(GSLocalMemory::m_psm[color_psm].bpp >= 16 || (/*possible_shuffle &&*/ GSLocalMemory::m_psm[color_psm].bpp == 8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 32)) && // Channel shuffles or non indexed lookups.
|
||||||
t->m_age <= 1 && (!found_t || t->m_last_draw > dst->m_last_draw) && CanTranslate(bp, bw, psm, block_boundary_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW))
|
t->m_age <= 1 && (!found_t || t->m_last_draw > dst->m_last_draw) /*&& CanTranslate(bp, bw, psm, block_boundary_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW)*/)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!t->HasValidBitsForFormat(psm, req_color, req_alpha) && !(possible_shuffle && GSLocalMemory::m_psm[psm].bpp == 16 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 32))
|
if (!t->HasValidBitsForFormat(psm, req_color, req_alpha) && !(possible_shuffle && GSLocalMemory::m_psm[psm].bpp == 16 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 32))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
u32 horz_page_offset = ((bp - t->m_TEX0.TBP0) >> 5) % t->m_TEX0.TBW;
|
||||||
|
if (GSLocalMemory::m_psm[color_psm].bpp == 16 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 32 && bw != 1 &&
|
||||||
|
((t->m_TEX0.TBW < (horz_page_offset + ((block_boundary_rect.z + GSLocalMemory::m_psm[psm].pgs.x - 1) / GSLocalMemory::m_psm[psm].pgs.x)) ||
|
||||||
|
(t->m_TEX0.TBW != bw && block_boundary_rect.w > GSLocalMemory::m_psm[psm].pgs.y))))
|
||||||
|
{
|
||||||
|
DevCon.Warning("BP %x - 16bit bad match for target bp %x bw %d src %d format %d", bp, t->m_TEX0.TBP0, t->m_TEX0.TBW, bw, t->m_TEX0.PSM);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Keep note that 2 bw is basically 1 normal page, as bw is in 64 pixels, and 8bit pages are 128 pixels wide, aka 2 bw.
|
||||||
|
else if (!possible_shuffle && (GSLocalMemory::m_psm[color_psm].bpp == 8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 32 &&
|
||||||
|
!((t->m_TEX0.TBW == (bw / 2)) || (((bw + 1) / 2) <= t->m_TEX0.TBW && (block_boundary_rect.w <= GSLocalMemory::m_psm[psm].pgs.y)))))
|
||||||
|
{
|
||||||
|
DevCon.Warning("BP %x - 8bit bad match for target bp %x bw %d src %d format %d", bp, t->m_TEX0.TBP0, t->m_TEX0.TBW, bw, t->m_TEX0.PSM);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// PSM equality needed because CreateSource does not handle PSM conversion.
|
// PSM equality needed because CreateSource does not handle PSM conversion.
|
||||||
// Only inclusive hit to limit false hits.
|
// Only inclusive hit to limit false hits.
|
||||||
GSVector4i rect = req_rect;
|
GSVector4i rect = req_rect;
|
||||||
|
@ -1478,7 +1507,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||||
}
|
}
|
||||||
if (bp > t->m_TEX0.TBP0)
|
if (bp > t->m_TEX0.TBP0)
|
||||||
{
|
{
|
||||||
GSVector4i new_rect = possible_shuffle ? block_boundary_rect : rect;
|
GSVector4i new_rect = (GSLocalMemory::m_psm[color_psm].bpp != GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp) ? block_boundary_rect : rect;
|
||||||
if (linear)
|
if (linear)
|
||||||
{
|
{
|
||||||
new_rect.z -= 1;
|
new_rect.z -= 1;
|
||||||
|
@ -1523,9 +1552,9 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||||
rect.y -= new_rect.y & ~(page_size.y - 1);
|
rect.y -= new_rect.y & ~(page_size.y - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
rect = rect.rintersect(t->m_valid);
|
//rect = rect.rintersect(t->m_valid);
|
||||||
|
|
||||||
if (rect.rempty())
|
if (rect.rintersect(t->m_valid).rempty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!t->m_dirty.empty())
|
if (!t->m_dirty.empty())
|
||||||
|
@ -1583,15 +1612,18 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||||
// Omitting that check here seemed less risky than blowing CS targets out...
|
// Omitting that check here seemed less risky than blowing CS targets out...
|
||||||
const GSVector2i& page_size = GSLocalMemory::m_psm[src_psm].pgs;
|
const GSVector2i& page_size = GSLocalMemory::m_psm[src_psm].pgs;
|
||||||
const GSOffset offset(GSLocalMemory::m_psm[src_psm].info, bp, bw, psm);
|
const GSOffset offset(GSLocalMemory::m_psm[src_psm].info, bp, bw, psm);
|
||||||
|
const u32 offset_bp = offset.bn(region.GetMinX(), region.GetMinY());
|
||||||
if (bp < t->m_TEX0.TBP0 && region.HasX() && region.HasY() &&
|
if (bp < t->m_TEX0.TBP0 && region.HasX() && region.HasY() &&
|
||||||
(region.GetMinX() & (page_size.x - 1)) == 0 && (region.GetMinY() & (page_size.y - 1)) == 0 &&
|
(region.GetMinX() & (page_size.x - 1)) == 0 && (region.GetMinY() & (page_size.y - 1)) == 0 &&
|
||||||
offset.bn(region.GetMinX(), region.GetMinY()) == t->m_TEX0.TBP0)
|
(offset.bn(region.GetMinX(), region.GetMinY()) == t->m_TEX0.TBP0 ||
|
||||||
|
((offset_bp >= t->m_TEX0.TBP0) && ((((offset_bp - t->m_TEX0.TBP0) >> 5) % bw) + (rect.width() / page_size.x)) <= bw)))
|
||||||
{
|
{
|
||||||
GL_CACHE("TC: Target 0x%x detected in front of TBP 0x%x with %d,%d offset (%d pages)",
|
GL_CACHE("TC: Target 0x%x detected in front of TBP 0x%x with %d,%d offset (%d pages)",
|
||||||
t->m_TEX0.TBP0, TEX0.TBP0, region.GetMinX(), region.GetMinY(),
|
t->m_TEX0.TBP0, TEX0.TBP0, region.GetMinX(), region.GetMinY(),
|
||||||
(region.GetMinY() / page_size.y) * TEX0.TBW + (region.GetMinX() / page_size.x));
|
(region.GetMinY() / page_size.y) * TEX0.TBW + (region.GetMinX() / page_size.x));
|
||||||
x_offset = -region.GetMinX();
|
|
||||||
y_offset = -region.GetMinY();
|
x_offset = ((((offset_bp - t->m_TEX0.TBP0) >> 5) % bw) * page_size.x) - region.GetMinX();
|
||||||
|
y_offset = ((((offset_bp - t->m_TEX0.TBP0) >> 5) / bw) * page_size.y) - region.GetMinY();
|
||||||
dst = t;
|
dst = t;
|
||||||
tex_merge_rt = false;
|
tex_merge_rt = false;
|
||||||
found_t = true;
|
found_t = true;
|
||||||
|
@ -1644,12 +1676,6 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||||
|
|
||||||
if (!found_t && !dst && !GSConfig.UserHacks_DisableDepthSupport)
|
if (!found_t && !dst && !GSConfig.UserHacks_DisableDepthSupport)
|
||||||
{
|
{
|
||||||
GSVector4i new_rect = req_rect;
|
|
||||||
|
|
||||||
// Just in case the TextureMinMax trolls us as it does, when checking if inside the target.
|
|
||||||
new_rect.z -= 2;
|
|
||||||
new_rect.w -= 2;
|
|
||||||
|
|
||||||
// Let's try a trick to avoid to use wrongly a depth buffer
|
// Let's try a trick to avoid to use wrongly a depth buffer
|
||||||
// Unfortunately, I don't have any Arc the Lad testcase
|
// Unfortunately, I don't have any Arc the Lad testcase
|
||||||
//
|
//
|
||||||
|
@ -1658,7 +1684,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||||
{
|
{
|
||||||
for (auto t : m_dst[DepthStencil])
|
for (auto t : m_dst[DepthStencil])
|
||||||
{
|
{
|
||||||
if (t->m_age <= 1 && t->m_used && t->m_dirty.empty() && GSUtil::HasSharedBits(psm, t->m_TEX0.PSM) && t->Inside(bp, bw, psm, new_rect))
|
if (t->m_age <= 1 && t->m_used && t->m_dirty.empty() && GSUtil::HasSharedBits(psm, t->m_TEX0.PSM) && t->Inside(bp, bw, psm, block_boundary_rect))
|
||||||
{
|
{
|
||||||
GL_INS("TC: Warning depth format read as color format. Pixels will be scrambled");
|
GL_INS("TC: Warning depth format read as color format. Pixels will be scrambled");
|
||||||
// Let's fetch a depth format texture. Rational, it will avoid the texture allocation and the
|
// Let's fetch a depth format texture. Rational, it will avoid the texture allocation and the
|
||||||
|
@ -1668,7 +1694,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||||
GIFRegTEX0 depth_TEX0;
|
GIFRegTEX0 depth_TEX0;
|
||||||
depth_TEX0.U32[0] = TEX0.U32[0] | (0x30u << 20u);
|
depth_TEX0.U32[0] = TEX0.U32[0] | (0x30u << 20u);
|
||||||
depth_TEX0.U32[1] = TEX0.U32[1];
|
depth_TEX0.U32[1] = TEX0.U32[1];
|
||||||
src = LookupDepthSource(false, depth_TEX0, TEXA, CLAMP, req_rect, possible_shuffle, linear, frame_fbp, req_color, req_alpha);
|
src = LookupDepthSource(false, depth_TEX0, TEXA, CLAMP, block_boundary_rect, possible_shuffle, linear, frame_fbp, req_color, req_alpha);
|
||||||
|
|
||||||
if (src != nullptr)
|
if (src != nullptr)
|
||||||
{
|
{
|
||||||
|
@ -1690,7 +1716,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
src = LookupDepthSource(false, TEX0, TEXA, CLAMP, req_rect, possible_shuffle, linear, frame_fbp, req_color, req_alpha, true);
|
src = LookupDepthSource(false, TEX0, TEXA, CLAMP, block_boundary_rect, possible_shuffle, linear, frame_fbp, req_color, req_alpha, true);
|
||||||
|
|
||||||
if (src != nullptr)
|
if (src != nullptr)
|
||||||
{
|
{
|
||||||
|
@ -1803,7 +1829,8 @@ GSVector2i GSTextureCache::ScaleRenderTargetSize(const GSVector2i& sz, float sca
|
||||||
}
|
}
|
||||||
|
|
||||||
GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type,
|
GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type,
|
||||||
bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_rgb, bool preserve_alpha, const GSVector4i draw_rect, bool is_shuffle, bool possible_clear, bool preserve_scale)
|
bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_rgb, bool preserve_alpha, const GSVector4i draw_rect,
|
||||||
|
bool is_shuffle, bool possible_clear, bool preserve_scale, GSTextureCache::Source* src, int offset)
|
||||||
{
|
{
|
||||||
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM];
|
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM];
|
||||||
const u32 bp = TEX0.TBP0;
|
const u32 bp = TEX0.TBP0;
|
||||||
|
@ -1812,8 +1839,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||||
const GSVector4 sRect(0, 0, 1, 1);
|
const GSVector4 sRect(0, 0, 1, 1);
|
||||||
GSVector4 dRect{};
|
GSVector4 dRect{};
|
||||||
bool clear = true;
|
bool clear = true;
|
||||||
const auto& calcRescale = [&size, &scale, &new_size, &new_scaled_size, &clear, &dRect](const Target* tgt)
|
const auto& calcRescale = [&size, &scale, &new_size, &new_scaled_size, &clear, &dRect](const Target* tgt) {
|
||||||
{
|
|
||||||
// TODO Possible optimization: rescale only the validity rectangle of the old target texture into the new one.
|
// TODO Possible optimization: rescale only the validity rectangle of the old target texture into the new one.
|
||||||
clear = (size.x > tgt->m_unscaled_size.x || size.y > tgt->m_unscaled_size.y);
|
clear = (size.x > tgt->m_unscaled_size.x || size.y > tgt->m_unscaled_size.y);
|
||||||
new_size = size.max(tgt->m_unscaled_size);
|
new_size = size.max(tgt->m_unscaled_size);
|
||||||
|
@ -1827,16 +1853,25 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||||
Target* dst = nullptr;
|
Target* dst = nullptr;
|
||||||
auto& list = m_dst[type];
|
auto& list = m_dst[type];
|
||||||
|
|
||||||
|
const GSVector4i min_rect = draw_rect.max_u32(GSVector4i(0, 0, draw_rect.x, draw_rect.y));
|
||||||
// TODO: Move all frame stuff to its own routine too.
|
// TODO: Move all frame stuff to its own routine too.
|
||||||
if (!is_frame)
|
if (!is_frame)
|
||||||
{
|
{
|
||||||
for (auto i = list.begin(); i != list.end(); ++i)
|
for (auto i = list.begin(); i != list.end();)
|
||||||
{
|
{
|
||||||
Target* t = *i;
|
Target* t = *i;
|
||||||
|
|
||||||
if (bp == t->m_TEX0.TBP0)
|
if (bp == t->m_TEX0.TBP0)
|
||||||
{
|
{
|
||||||
bool can_use = true;
|
bool can_use = true;
|
||||||
|
|
||||||
|
if (dst && (GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw))
|
||||||
|
{
|
||||||
|
DevCon.Warning("Ignoring target at %x as one at %x is newer", t->m_TEX0.TBP0, dst->m_TEX0.TBP0);
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// if It's an old target and it's being completely overwritten, kill it.
|
// if It's an old target and it's being completely overwritten, kill it.
|
||||||
// Dragon Quest 8 reuses a render-target sized buffer as a single-page buffer, without clearing it. But,
|
// Dragon Quest 8 reuses a render-target sized buffer as a single-page buffer, without clearing it. But,
|
||||||
// it does dirty it by writing over the 64x64 region. So while we can't use this heuristic for tossing
|
// it does dirty it by writing over the 64x64 region. So while we can't use this heuristic for tossing
|
||||||
|
@ -1880,16 +1915,72 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||||
dst = t;
|
dst = t;
|
||||||
|
|
||||||
dst->m_32_bits_fmt |= (psm_s.bpp != 16);
|
dst->m_32_bits_fmt |= (psm_s.bpp != 16);
|
||||||
|
|
||||||
|
/*if (FindOverlappingTarget(dst))
|
||||||
|
continue;
|
||||||
|
else*/
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else if(!(src && src->m_from_target == t))
|
||||||
{
|
{
|
||||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to change in target", t->m_TEX0.TBP0, t->m_TEX0.TBW, psm_str(t->m_TEX0.PSM));
|
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to change in target", t->m_TEX0.TBP0, t->m_TEX0.TBW, psm_str(t->m_TEX0.PSM));
|
||||||
InvalidateSourcesFromTarget(t);
|
InvalidateSourcesFromTarget(t);
|
||||||
i = list.erase(i);
|
i = list.erase(i);
|
||||||
delete t;
|
delete t;
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Probably pointing to half way through the target
|
||||||
|
else if (!min_rect.rempty() && GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets)
|
||||||
|
{
|
||||||
|
// Problem: Project - Snowblind and Tomb Raider offset the RT but not the Z
|
||||||
|
/*if (offset != -1 && (bp - t->m_TEX0.TBP0) != offset)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
const u32 widthpage_offset = (std::abs(static_cast<int>(bp - t->m_TEX0.TBP0)) >> 5) % std::max(t->m_TEX0.TBW, 1U);
|
||||||
|
/*const bool is_aligned_ok = widthpage_offset == 0 || (t->m_TEX0.TBW == TEX0.TBW &&
|
||||||
|
((((min_rect.z + 63) >> 6) + widthpage_offset) <= TEX0.TBW) ||
|
||||||
|
((widthpage_offset + TEX0.TBW) <= t->m_TEX0.TBW) ||
|
||||||
|
min_rect.width() <= 64 || (widthpage_offset == (t->m_TEX0.TBW >> 1) &&
|
||||||
|
(static_cast<u32>(min_rect.width()) <= (widthpage_offset * 64))));*/
|
||||||
|
const bool is_aligned_ok = widthpage_offset == 0 || ((min_rect.width() <= static_cast<int>((t->m_TEX0.TBW - widthpage_offset) * 64) && (t->m_TEX0.TBW == TEX0.TBW || TEX0.TBW == 1)) && bp >= t->m_TEX0.TBP0);
|
||||||
|
const bool no_target_or_newer = (!dst || ((GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw)));
|
||||||
|
const bool width_match = (t->m_TEX0.TBW == TEX0.TBW || (TEX0.TBW == 1 && draw_rect.w <= GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y));
|
||||||
|
// if it's a shuffle, some games tend to offset back by a page, such as Tomb Raider, for no disernable reason, but it then causes problems.
|
||||||
|
// This can also happen horizontally (Catwoman moves everything one page left with shuffles), but this is too messy to deal with right now.
|
||||||
|
const bool overlaps = t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect) || (is_shuffle && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 8 && t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect + GSVector4i(0, 0, 0, 32)));
|
||||||
|
if (no_target_or_newer && is_aligned_ok && width_match && overlaps)
|
||||||
|
{
|
||||||
|
const GSLocalMemory::psm_t& s_psm = GSLocalMemory::m_psm[TEX0.PSM];
|
||||||
|
|
||||||
|
if (!is_shuffle && (!GSUtil::HasSameSwizzleBits(t->m_TEX0.PSM, TEX0.PSM) ||
|
||||||
|
(widthpage_offset % std::max(t->m_TEX0.TBW, 1U)) != 0 && ((widthpage_offset + (min_rect.width() + (s_psm.pgs.x - 1)) / s_psm.pgs.x)) > t->m_TEX0.TBW))
|
||||||
|
{
|
||||||
|
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to change in target", t->m_TEX0.TBP0, t->m_TEX0.TBW, psm_str(t->m_TEX0.PSM));
|
||||||
|
InvalidateSourcesFromTarget(t);
|
||||||
|
i = list.erase(i);
|
||||||
|
delete t;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (t->m_dirty.empty())
|
||||||
|
{
|
||||||
|
//DevCon.Warning("Here draw %d wanted %x PSM %x got %x PSM %x offset of %d pages width %d pages draw width %d", GSState::s_n, bp, TEX0.PSM, t->m_TEX0.TBP0, t->m_TEX0.PSM, (bp - t->m_TEX0.TBP0) >> 5, t->m_TEX0.TBW, draw_rect.width());
|
||||||
|
dst = t;
|
||||||
|
|
||||||
|
dst->m_32_bits_fmt |= (psm_s.bpp != 16);
|
||||||
|
//Continue just in case there's a newer target
|
||||||
|
if (used)
|
||||||
|
list.MoveFront(i.Index());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -2034,6 +2125,8 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||||
{
|
{
|
||||||
calcRescale(dst);
|
calcRescale(dst);
|
||||||
GSTexture* tex = g_gs_device->CreateDepthStencil(new_scaled_size.x, new_scaled_size.y, GSTexture::Format::DepthStencil, false);
|
GSTexture* tex = g_gs_device->CreateDepthStencil(new_scaled_size.x, new_scaled_size.y, GSTexture::Format::DepthStencil, false);
|
||||||
|
if (!tex)
|
||||||
|
return nullptr;
|
||||||
g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, ShaderConvert::FLOAT32_TO_FLOAT24, false);
|
g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, ShaderConvert::FLOAT32_TO_FLOAT24, false);
|
||||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||||
g_gs_device->Recycle(dst->m_texture);
|
g_gs_device->Recycle(dst->m_texture);
|
||||||
|
@ -2042,6 +2135,69 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||||
dst->m_alpha_min = 0;
|
dst->m_alpha_min = 0;
|
||||||
dst->m_alpha_max = 0;
|
dst->m_alpha_max = 0;
|
||||||
}
|
}
|
||||||
|
else if (std::abs(static_cast<s16>(GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp - GSLocalMemory::m_psm[TEX0.PSM].bpp)) == 16)
|
||||||
|
{
|
||||||
|
dst->Update(false);
|
||||||
|
|
||||||
|
const bool scale_down = GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp > GSLocalMemory::m_psm[TEX0.PSM].bpp;
|
||||||
|
new_size = dst->m_unscaled_size;
|
||||||
|
new_scaled_size = ScaleRenderTargetSize(dst->m_unscaled_size, scale);
|
||||||
|
|
||||||
|
dRect = (GSVector4(GSVector4i::loadh(dst->m_unscaled_size)) * GSVector4(scale)).ceil();
|
||||||
|
if (!is_shuffle || GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16)
|
||||||
|
{
|
||||||
|
if (scale_down)
|
||||||
|
{
|
||||||
|
if ((new_size.y * 2) < 1024)
|
||||||
|
{
|
||||||
|
new_scaled_size.y *= 2;
|
||||||
|
new_size.y *= 2;
|
||||||
|
dst->m_valid.y *= 2;
|
||||||
|
dst->m_valid.w *= 2;
|
||||||
|
}
|
||||||
|
dRect.y *= 2;
|
||||||
|
dRect.w *= 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new_scaled_size.y /= 2;
|
||||||
|
new_size.y /= 2;
|
||||||
|
dRect.y /= 2;
|
||||||
|
dRect.w /= 2;
|
||||||
|
dst->m_valid.y /= 2;
|
||||||
|
dst->m_valid.w /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!is_shuffle)
|
||||||
|
{
|
||||||
|
GL_INS("TC Convert to 16bit: %dx%d: %dx%d @ %f -> %dx%d @ %f", dst->m_unscaled_size.x, dst->m_unscaled_size.y,
|
||||||
|
dst->m_texture->GetWidth(), dst->m_texture->GetHeight(), dst->m_scale, new_scaled_size.x, new_scaled_size.y,
|
||||||
|
scale);
|
||||||
|
|
||||||
|
if (src && src->m_from_target && src->m_from_target == dst)
|
||||||
|
{
|
||||||
|
src->m_texture = dst->m_texture;
|
||||||
|
src->m_target_direct = false;
|
||||||
|
src->m_shared_texture = false;
|
||||||
|
|
||||||
|
dst->ResizeTexture(new_size.x, new_size.y, true, true, GSVector4i(dRect), true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dst->ResizeTexture(new_size.x, new_size.y, true, true, GSVector4i(dRect));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New format or doing a shuffle to a 32bit target that used to be 16bit
|
||||||
|
if (!is_shuffle || GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp < GSLocalMemory::m_psm[TEX0.PSM].bpp)
|
||||||
|
dst->m_TEX0.PSM = TEX0.PSM;
|
||||||
|
// LEGO Dome Racers does a copy to a target as 8bit in alpha only, this doesn't really work great for us, so let's make it 32bit with invalid RGB.
|
||||||
|
else if (dst->m_TEX0.PSM == PSMT8H)
|
||||||
|
{
|
||||||
|
dst->m_TEX0.PSM = PSMCT32;
|
||||||
|
dst->m_valid_rgb = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If our RGB was invalidated, we need to pull it from depth.
|
// If our RGB was invalidated, we need to pull it from depth.
|
||||||
// Terminator 3 will reuse our dst_matched target with the RGB masked, then later use the full ARGB area, so we need to update the depth.
|
// Terminator 3 will reuse our dst_matched target with the RGB masked, then later use the full ARGB area, so we need to update the depth.
|
||||||
|
@ -2165,7 +2321,15 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// If the format is completely different, but it's the same location, it's likely just overwriting it, so get rid.
|
||||||
|
if (!is_shuffle && t->m_TEX0.TBW != TEX0.TBW && TEX0.TBW != 1 && !preserve_rgb && min_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y)
|
||||||
|
{
|
||||||
|
DevCon.Warning("Deleting Z draw %d", GSState::s_n);
|
||||||
|
InvalidateSourcesFromTarget(t);
|
||||||
|
i = rev_list.erase(i);
|
||||||
|
delete t;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const GSLocalMemory::psm_t& t_psm_s = GSLocalMemory::m_psm[t->m_TEX0.PSM];
|
const GSLocalMemory::psm_t& t_psm_s = GSLocalMemory::m_psm[t->m_TEX0.PSM];
|
||||||
if (t_psm_s.bpp != psm_s.bpp)
|
if (t_psm_s.bpp != psm_s.bpp)
|
||||||
{
|
{
|
||||||
|
@ -2242,6 +2406,8 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||||
dst->m_valid_alpha_high = dst_match->m_valid_alpha_high; //&& psm_s.trbpp != 24;
|
dst->m_valid_alpha_high = dst_match->m_valid_alpha_high; //&& psm_s.trbpp != 24;
|
||||||
dst->m_valid_rgb = dst_match->m_valid_rgb;
|
dst->m_valid_rgb = dst_match->m_valid_rgb;
|
||||||
dst->m_was_dst_matched = true;
|
dst->m_was_dst_matched = true;
|
||||||
|
dst_match->m_was_dst_matched = true;
|
||||||
|
dst_match->m_valid_rgb = preserve_rgb;
|
||||||
|
|
||||||
if (GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp > 16)
|
if (GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp > 16)
|
||||||
dst->m_TEX0.TBW = dst_match->m_TEX0.TBW; // Be careful of shuffles of the depth as C16, but using a buffer width of 16 (Mercenaries).
|
dst->m_TEX0.TBW = dst_match->m_TEX0.TBW; // Be careful of shuffles of the depth as C16, but using a buffer width of 16 (Mercenaries).
|
||||||
|
@ -2605,8 +2771,8 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
|
||||||
auto j = i;
|
auto j = i;
|
||||||
Target* t = *j;
|
Target* t = *j;
|
||||||
|
|
||||||
if (dst != t && t->m_TEX0.PSM == dst->m_TEX0.PSM/* && t->m_TEX0.TBW == dst->m_TEX0.TBW*/)
|
if (dst != t && t->m_TEX0.PSM == dst->m_TEX0.PSM && t->Overlaps(dst->m_TEX0.TBP0, dst->m_TEX0.TBW, dst->m_TEX0.PSM, dst->m_valid) &&
|
||||||
if (t->Overlaps(dst->m_TEX0.TBP0, dst->m_TEX0.TBW, dst->m_TEX0.PSM, dst->m_valid))
|
static_cast<int>(((t->m_TEX0.TBP0 - dst->m_TEX0.TBP0) / 32) % std::max(dst->m_TEX0.TBW, 1U)) <= std::max(0, static_cast<int>(dst->m_TEX0.TBW - t->m_TEX0.TBW)))
|
||||||
{
|
{
|
||||||
const u32 buffer_width = std::max(1U, dst->m_TEX0.TBW);
|
const u32 buffer_width = std::max(1U, dst->m_TEX0.TBW);
|
||||||
|
|
||||||
|
@ -2667,6 +2833,7 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
|
||||||
|
|
||||||
GL_INS("RT double buffer copy from FBP 0x%x, %dx%d => %d,%d", t->m_TEX0.TBP0, copy_width, copy_height, 0, dst_offset_scaled_height);
|
GL_INS("RT double buffer copy from FBP 0x%x, %dx%d => %d,%d", t->m_TEX0.TBP0, copy_width, copy_height, 0, dst_offset_scaled_height);
|
||||||
|
|
||||||
|
|
||||||
// Clear the dirty first
|
// Clear the dirty first
|
||||||
t->Update();
|
t->Update();
|
||||||
dst->Update();
|
dst->Update();
|
||||||
|
@ -2910,7 +3077,7 @@ void GSTextureCache::ScaleTargetForDisplay(Target* t, const GIFRegTEX0& dispfb,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject the new size back into the cache.
|
// Inject the new size back into the cache.
|
||||||
GetTargetSize(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, 0, static_cast<u32>(needed_height));
|
GetTargetSize(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, new_width, static_cast<u32>(needed_height));
|
||||||
}
|
}
|
||||||
|
|
||||||
float GSTextureCache::ConvertColorToDepth(u32 c, ShaderConvert convert)
|
float GSTextureCache::ConvertColorToDepth(u32 c, ShaderConvert convert)
|
||||||
|
@ -3061,7 +3228,67 @@ bool GSTextureCache::PrepareDownloadTexture(u32 width, u32 height, GSTexture::Fo
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 write_psm)
|
/*void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 write_psm, u32 write_bw)
|
||||||
|
{
|
||||||
|
const bool preserve_alpha = (GSLocalMemory::m_psm[write_psm].trbpp == 24);
|
||||||
|
for (int type = 0; type < 2; type++)
|
||||||
|
{
|
||||||
|
auto& list = m_dst[type];
|
||||||
|
for (auto i = list.begin(); i != list.end();)
|
||||||
|
{
|
||||||
|
Target* const t = *i;
|
||||||
|
if ((start_bp > t->UnwrappedEndBlock() || end_bp < t->m_TEX0.TBP0) || (start_bp != t->m_TEX0.TBP0 && (t->m_TEX0.TBP0 < start_bp || t->UnwrappedEndBlock() > end_bp) && t->m_dirty.empty()))
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//const u32 total_pages = ((end_bp + 1) - t->m_TEX0.TBP0) >> 5;
|
||||||
|
// Not covering the whole target, and a different format, so just dirty it.
|
||||||
|
//if (start_bp >= t->m_TEX0.TBP0 && (t->UnwrappedEndBlock() > end_bp) && write_psm != t->m_TEX0.PSM && write_bw == t->m_TEX0.TBW)
|
||||||
|
//{
|
||||||
|
// const GSLocalMemory::psm_t& target_psm = GSLocalMemory::m_psm[write_psm];
|
||||||
|
// const u32 page_offset = ((start_bp - t->m_TEX0.TBP0) >> 5);
|
||||||
|
// const u32 vertical_offset = (page_offset / t->m_TEX0.TBW) * target_psm.pgs.y;
|
||||||
|
// GSVector4i dirty_area = GSVector4i(page_offset % t->m_TEX0.TBW, vertical_offset, t->m_valid.z, vertical_offset + ((total_pages / t->m_TEX0.TBW) * target_psm.pgs.y));
|
||||||
|
// InvalidateVideoMem(g_gs_renderer->m_mem.GetOffset(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM), dirty_area, true);
|
||||||
|
// ++i;
|
||||||
|
// continue;
|
||||||
|
//}
|
||||||
|
|
||||||
|
InvalidateSourcesFromTarget(t);
|
||||||
|
|
||||||
|
t->m_valid_alpha_low &= preserve_alpha;
|
||||||
|
t->m_valid_alpha_high &= preserve_alpha;
|
||||||
|
t->m_valid_rgb &= !(t->m_TEX0.TBP0 == start_bp);
|
||||||
|
|
||||||
|
// Don't keep partial depth buffers around.
|
||||||
|
if ((!t->m_valid_alpha_low && !t->m_valid_alpha_high && !t->m_valid_rgb) || type == DepthStencil)
|
||||||
|
{
|
||||||
|
auto& rev_list = m_dst[1 - type];
|
||||||
|
for (auto j = rev_list.begin(); j != rev_list.end();)
|
||||||
|
{
|
||||||
|
Target* const rev_t = *j;
|
||||||
|
if (rev_t->m_TEX0.TBP0 == t->m_TEX0.TBP0 && GSLocalMemory::m_psm[rev_t->m_TEX0.PSM].bpp == GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp)
|
||||||
|
{
|
||||||
|
rev_t->m_was_dst_matched = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_CACHE("TC: InvalidateContainedTargets: Remove Target %s[%x, %s]", to_string(type), t->m_TEX0.TBP0, psm_str(t->m_TEX0.PSM));
|
||||||
|
i = list.erase(i);
|
||||||
|
delete t;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_CACHE("TC: InvalidateContainedTargets: Clear RGB valid on %s[%x, %s]", to_string(type), t->m_TEX0.TBP0, psm_str(t->m_TEX0.PSM));
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 write_psm, u32 write_bw)
|
||||||
{
|
{
|
||||||
const bool preserve_alpha = (GSLocalMemory::m_psm[write_psm].trbpp == 24);
|
const bool preserve_alpha = (GSLocalMemory::m_psm[write_psm].trbpp == 24);
|
||||||
for (int type = 0; type < 2; type++)
|
for (int type = 0; type < 2; type++)
|
||||||
|
@ -3178,6 +3405,12 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
|
||||||
const u32 bw = off.bw();
|
const u32 bw = off.bw();
|
||||||
const u32 psm = off.psm();
|
const u32 psm = off.psm();
|
||||||
|
|
||||||
|
// Get the bounds that we're invalidating in blocks, so we can remove any targets which are completely contained.
|
||||||
|
// Unfortunately sometimes the draw rect is incorrect, and since the end block gets the rect -1, it'll underflow,
|
||||||
|
// so we need to prevent that from happening. Just make it a single block in that case, and hope for the best.
|
||||||
|
const u32 start_bp = GSLocalMemory::GetStartBlockAddress(off.bp(), off.bw(), off.psm(), rect);
|
||||||
|
const u32 end_bp = rect.rempty() ? start_bp : GSLocalMemory::GetUnwrappedEndBlockAddress(off.bp(), off.bw(), off.psm(), rect);
|
||||||
|
|
||||||
if (!target)
|
if (!target)
|
||||||
{
|
{
|
||||||
// Remove Source that have same BP as the render target (color&dss)
|
// Remove Source that have same BP as the render target (color&dss)
|
||||||
|
@ -3188,7 +3421,7 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
|
||||||
Source* s = *i;
|
Source* s = *i;
|
||||||
++i;
|
++i;
|
||||||
|
|
||||||
if (GSUtil::HasSharedBits(bp, psm, s->m_TEX0.TBP0, s->m_TEX0.PSM) ||
|
if ((GSUtil::HasSharedBits(psm, s->m_TEX0.PSM) && (bp >= start_bp && bp < end_bp)) ||
|
||||||
(GSUtil::HasSharedBits(bp, psm, s->m_from_target_TEX0.TBP0, s->m_TEX0.PSM) && s->m_target))
|
(GSUtil::HasSharedBits(bp, psm, s->m_from_target_TEX0.TBP0, s->m_TEX0.PSM) && s->m_target))
|
||||||
{
|
{
|
||||||
m_src.RemoveAt(s);
|
m_src.RemoveAt(s);
|
||||||
|
@ -3220,8 +3453,7 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
|
||||||
// But this causes rects to be too big, especially in WRC games, I don't think there's any need to align them here.
|
// But this causes rects to be too big, especially in WRC games, I don't think there's any need to align them here.
|
||||||
GSVector4i r = rect;
|
GSVector4i r = rect;
|
||||||
|
|
||||||
off.loopPages(rect, [this, &rect, bp, bw, psm, &found](u32 page)
|
off.loopPages(rect, [this, &rect, bp, bw, psm, &found](u32 page) {
|
||||||
{
|
|
||||||
auto& list = m_src.m_map[page];
|
auto& list = m_src.m_map[page];
|
||||||
for (auto i = list.begin(); i != list.end();)
|
for (auto i = list.begin(); i != list.end();)
|
||||||
{
|
{
|
||||||
|
@ -3286,11 +3518,6 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
|
||||||
if (!target)
|
if (!target)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Get the bounds that we're invalidating in blocks, so we can remove any targets which are completely contained.
|
|
||||||
// Unfortunately sometimes the draw rect is incorrect, and since the end block gets the rect -1, it'll underflow,
|
|
||||||
// so we need to prevent that from happening. Just make it a single block in that case, and hope for the best.
|
|
||||||
const u32 start_bp = GSLocalMemory::GetStartBlockAddress(off.bp(), off.bw(), off.psm(), rect);
|
|
||||||
const u32 end_bp = rect.rempty() ? start_bp : GSLocalMemory::GetUnwrappedEndBlockAddress(off.bp(), off.bw(), off.psm(), rect);
|
|
||||||
|
|
||||||
RGBAMask rgba;
|
RGBAMask rgba;
|
||||||
rgba._u32 = GSUtil::GetChannelMask(psm);
|
rgba._u32 = GSUtil::GetChannelMask(psm);
|
||||||
|
@ -3844,7 +4071,7 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
|
||||||
// Make sure the copy doesn't go out of bounds (it shouldn't).
|
// Make sure the copy doesn't go out of bounds (it shouldn't).
|
||||||
if ((scaled_dx + scaled_w) > dst->m_texture->GetWidth() || (scaled_dy + scaled_h) > dst->m_texture->GetHeight())
|
if ((scaled_dx + scaled_w) > dst->m_texture->GetWidth() || (scaled_dy + scaled_h) > dst->m_texture->GetHeight())
|
||||||
return false;
|
return false;
|
||||||
GL_CACHE("HW Move 0x%x[BW:%u PSM:%s] to 0x%x[BW:%u PSM:%s] <%d,%d->%d,%d> -> <%d,%d->%d,%d>", SBP, SBW,
|
GL_CACHE("HW Move after draw %d 0x%x[BW:%u PSM:%s] to 0x%x[BW:%u PSM:%s] <%d,%d->%d,%d> -> <%d,%d->%d,%d>", GSState::s_n, SBP, SBW,
|
||||||
psm_str(SPSM), DBP, DBW, psm_str(DPSM), sx, sy, sx + w, sy + h, dx, dy, dx + w, dy + h);
|
psm_str(SPSM), DBP, DBW, psm_str(DPSM), sx, sy, sx + w, sy + h, dx, dy, dx + w, dy + h);
|
||||||
|
|
||||||
const bool cover_whole_target = dst->m_type == RenderTarget && GSVector4i(dx, dy, dx + w, dy + h).rintersect(dst->m_valid).eq(dst->m_valid);
|
const bool cover_whole_target = dst->m_type == RenderTarget && GSVector4i(dx, dy, dx + w, dy + h).rintersect(dst->m_valid).eq(dst->m_valid);
|
||||||
|
@ -3970,6 +4197,7 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
|
||||||
|
|
||||||
// Invalidate any sources that overlap with the target (since they're now stale).
|
// Invalidate any sources that overlap with the target (since they're now stale).
|
||||||
InvalidateVideoMem(g_gs_renderer->m_mem.GetOffset(DBP, DBW, DPSM), GSVector4i(dx, dy, dx + w, dy + h), false);
|
InvalidateVideoMem(g_gs_renderer->m_mem.GetOffset(DBP, DBW, DPSM), GSVector4i(dx, dy, dx + w, dy + h), false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4155,8 +4383,8 @@ GSTextureCache::Target* GSTextureCache::GetExactTarget(u32 BP, u32 BW, int type,
|
||||||
for (auto it = rts.begin(); it != rts.end(); ++it) // Iterate targets from MRU to LRU.
|
for (auto it = rts.begin(); it != rts.end(); ++it) // Iterate targets from MRU to LRU.
|
||||||
{
|
{
|
||||||
Target* t = *it;
|
Target* t = *it;
|
||||||
|
const u32 tgt_bw = std::max(t->m_TEX0.TBW, 1U);
|
||||||
if (t->m_TEX0.TBP0 == BP && t->m_TEX0.TBW == BW && t->UnwrappedEndBlock() >= end_bp)
|
if ((t->m_TEX0.TBP0 == BP || (GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets && t->m_TEX0.TBP0 < BP && ((BP >> 5) % tgt_bw) == 0)) && tgt_bw == BW && t->UnwrappedEndBlock() >= end_bp)
|
||||||
{
|
{
|
||||||
rts.MoveFront(it.Index());
|
rts.MoveFront(it.Index());
|
||||||
return t;
|
return t;
|
||||||
|
@ -4380,8 +4608,11 @@ void GSTextureCache::ReplaceSourceTexture(Source* s, GSTexture* new_texture, flo
|
||||||
if (s->m_from_hash_cache)
|
if (s->m_from_hash_cache)
|
||||||
s->m_from_hash_cache->refcount++;
|
s->m_from_hash_cache->refcount++;
|
||||||
else if (!s->m_shared_texture)
|
else if (!s->m_shared_texture)
|
||||||
|
{
|
||||||
|
DevCon.Warning("replace %d", m_source_memory_usage);
|
||||||
m_source_memory_usage += s->m_texture->GetMemUsage();
|
m_source_memory_usage += s->m_texture->GetMemUsage();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GSTextureCache::IncAge()
|
void GSTextureCache::IncAge()
|
||||||
{
|
{
|
||||||
|
@ -4488,9 +4719,9 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hack = false;
|
bool hack = false;
|
||||||
bool channel_shuffle = false;
|
bool channel_shuffle = dst && (TEX0.PSM == PSMT8) && (GSRendererHW::GetInstance()->TestChannelShuffle(dst));
|
||||||
|
|
||||||
if (dst && (x_offset != 0 || y_offset != 0))
|
if (dst && (x_offset != 0 || y_offset != 0) && (TEX0.PSM != PSMT8 || channel_shuffle))
|
||||||
{
|
{
|
||||||
const float scale = dst->m_scale;
|
const float scale = dst->m_scale;
|
||||||
const int x = static_cast<int>(scale * x_offset);
|
const int x = static_cast<int>(scale * x_offset);
|
||||||
|
@ -4516,7 +4747,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_source_memory_usage += dTex->GetMemUsage();
|
m_target_memory_usage += dTex->GetMemUsage();
|
||||||
|
|
||||||
// copy the rt in
|
// copy the rt in
|
||||||
const GSVector4i area(GSVector4i(x, y, x + w, y + h).rintersect(GSVector4i(sTex->GetSize()).zwxy()));
|
const GSVector4i area(GSVector4i(x, y, x + w, y + h).rintersect(GSVector4i(sTex->GetSize()).zwxy()));
|
||||||
|
@ -4554,7 +4785,8 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||||
src->m_unscaled_size = dst->m_unscaled_size;
|
src->m_unscaled_size = dst->m_unscaled_size;
|
||||||
src->m_shared_texture = true;
|
src->m_shared_texture = true;
|
||||||
|
|
||||||
channel_shuffle = GSRendererHW::GetInstance()->TestChannelShuffle(dst);
|
if(channel_shuffle)
|
||||||
|
m_temporary_source = src;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate immediately on recursive draws, because if we don't here, InvalidateVideoMem() will.
|
// Invalidate immediately on recursive draws, because if we don't here, InvalidateVideoMem() will.
|
||||||
|
@ -4810,7 +5042,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||||
}
|
}
|
||||||
|
|
||||||
// kill source immediately if it's the RT/DS, because that'll get invalidated immediately
|
// kill source immediately if it's the RT/DS, because that'll get invalidated immediately
|
||||||
if (GSRendererHW::GetInstance()->IsTBPFrameOrZ(dst->m_TEX0.TBP0))
|
if (GSRendererHW::GetInstance()->IsTBPFrameOrZ(dst->m_TEX0.TBP0) || channel_shuffle)
|
||||||
{
|
{
|
||||||
GL_CACHE("TC: Source is RT or ZBUF, invalidating after draw.");
|
GL_CACHE("TC: Source is RT or ZBUF, invalidating after draw.");
|
||||||
m_temporary_source = src;
|
m_temporary_source = src;
|
||||||
|
@ -4833,7 +5065,9 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_source_memory_usage += dTex->GetMemUsage();
|
src->m_shared_texture = false;
|
||||||
|
src->m_target_direct = false;
|
||||||
|
m_target_memory_usage += dTex->GetMemUsage();
|
||||||
src->m_texture = dTex;
|
src->m_texture = dTex;
|
||||||
|
|
||||||
if (use_texture)
|
if (use_texture)
|
||||||
|
@ -4869,6 +5103,23 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||||
g_gs_device->ConvertToIndexedTexture(sTex, dst->m_scale, x_offset, y_offset,
|
g_gs_device->ConvertToIndexedTexture(sTex, dst->m_scale, x_offset, y_offset,
|
||||||
std::max<u32>(dst->m_TEX0.TBW, 1u) * 64, dst->m_TEX0.PSM, dTex,
|
std::max<u32>(dst->m_TEX0.TBW, 1u) * 64, dst->m_TEX0.PSM, dTex,
|
||||||
std::max<u32>(TEX0.TBW, 1u) * 64, TEX0.PSM);
|
std::max<u32>(TEX0.TBW, 1u) * 64, TEX0.PSM);
|
||||||
|
|
||||||
|
// Adjust the region for the newly translated rect.
|
||||||
|
u32 const dst_y_height = GSLocalMemory::m_psm[dst->m_TEX0.PSM].pgs.y;
|
||||||
|
u32 const src_y_height = GSLocalMemory::m_psm[TEX0.PSM].pgs.y;
|
||||||
|
u32 const dst_page_offset = (y_offset / dst_y_height) * std::max(dst->m_TEX0.TBW, 1U);
|
||||||
|
y_offset = (dst_page_offset / (std::max(TEX0.TBW / 2U, 1U))) * src_y_height;
|
||||||
|
|
||||||
|
u32 const src_page_width = GSLocalMemory::m_psm[TEX0.PSM].pgs.x;
|
||||||
|
x_offset = (x_offset / GSLocalMemory::m_psm[dst->m_TEX0.PSM].pgs.x) * GSLocalMemory::m_psm[TEX0.PSM].pgs.x;
|
||||||
|
if (x_offset >= static_cast<int>(std::max(TEX0.TBW, 1U) * src_page_width))
|
||||||
|
{
|
||||||
|
const u32 adjust = x_offset / src_page_width;
|
||||||
|
y_offset += adjust * GSLocalMemory::m_psm[TEX0.PSM].pgs.y;
|
||||||
|
x_offset -= src_page_width * adjust;
|
||||||
|
}
|
||||||
|
src->m_region.SetX(x_offset, x_offset + tw);
|
||||||
|
src->m_region.SetY(y_offset, y_offset + th);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -5020,8 +5271,10 @@ GSTextureCache::Source* GSTextureCache::CreateMergedSource(GIFRegTEX0 TEX0, GIFR
|
||||||
{
|
{
|
||||||
// We *should* be able to use the TBW here as an indicator of size... except Destroy All Humans 2 sets
|
// We *should* be able to use the TBW here as an indicator of size... except Destroy All Humans 2 sets
|
||||||
// TBW to 10, and samples from 64 through 703... which means it'd be grabbing the next row at the end.
|
// TBW to 10, and samples from 64 through 703... which means it'd be grabbing the next row at the end.
|
||||||
const int tex_width = std::max<int>(64 * TEX0.TBW, region.GetMaxX());
|
// Round the size up to the next block
|
||||||
const int tex_height = region.HasY() ? region.GetHeight() : (1 << TEX0.TH);
|
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM];
|
||||||
|
const int tex_width = (std::max<int>(64 * TEX0.TBW, region.GetMaxX()) + (psm_s.bs.x - 1)) & ~(psm_s.bs.x - 1);
|
||||||
|
const int tex_height = ((region.HasY() ? region.GetHeight() : (1 << TEX0.TH)) + (psm_s.bs.y - 1)) & ~(psm_s.bs.y - 1);
|
||||||
const int scaled_width = static_cast<int>(static_cast<float>(tex_width) * scale);
|
const int scaled_width = static_cast<int>(static_cast<float>(tex_width) * scale);
|
||||||
const int scaled_height = static_cast<int>(static_cast<float>(tex_height) * scale);
|
const int scaled_height = static_cast<int>(static_cast<float>(tex_height) * scale);
|
||||||
|
|
||||||
|
@ -5288,7 +5541,7 @@ GSTextureCache::Source* GSTextureCache::CreateMergedSource(GIFRegTEX0 TEX0, GIFR
|
||||||
Console.Error("Failed to allocate %dx%d merged dest texture", scaled_width, scaled_height);
|
Console.Error("Failed to allocate %dx%d merged dest texture", scaled_width, scaled_height);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
DevCon.Warning("Merged %d", m_source_memory_usage);
|
||||||
m_source_memory_usage += dtex->GetMemUsage();
|
m_source_memory_usage += dtex->GetMemUsage();
|
||||||
|
|
||||||
// Sort rect list by the texture, we want to batch as many as possible together.
|
// Sort rect list by the texture, we want to batch as many as possible together.
|
||||||
|
@ -5634,8 +5887,7 @@ std::shared_ptr<GSTextureCache::Palette> GSTextureCache::LookupPaletteObject(con
|
||||||
|
|
||||||
void GSTextureCache::Read(Target* t, const GSVector4i& r)
|
void GSTextureCache::Read(Target* t, const GSVector4i& r)
|
||||||
{
|
{
|
||||||
if ((!t->m_dirty.empty() && !t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size).rintersect(r).rempty())
|
if ((!t->m_dirty.empty() && !t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size).rintersect(r).rempty()) || r.width() == 0 || r.height() == 0)
|
||||||
|| r.width() == 0 || r.height() == 0)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const GIFRegTEX0& TEX0 = t->m_TEX0;
|
const GIFRegTEX0& TEX0 = t->m_TEX0;
|
||||||
|
@ -5856,6 +6108,9 @@ GSTextureCache::Source::~Source()
|
||||||
// to recycle.
|
// to recycle.
|
||||||
if (!m_shared_texture && !m_from_hash_cache && m_texture)
|
if (!m_shared_texture && !m_from_hash_cache && m_texture)
|
||||||
{
|
{
|
||||||
|
if(m_from_target)
|
||||||
|
g_texture_cache->m_target_memory_usage -= m_texture->GetMemUsage();
|
||||||
|
else
|
||||||
g_texture_cache->m_source_memory_usage -= m_texture->GetMemUsage();
|
g_texture_cache->m_source_memory_usage -= m_texture->GetMemUsage();
|
||||||
g_gs_device->Recycle(m_texture);
|
g_gs_device->Recycle(m_texture);
|
||||||
}
|
}
|
||||||
|
@ -6177,6 +6432,7 @@ GSTextureCache::Target::~Target()
|
||||||
{
|
{
|
||||||
// Targets should never be shared.
|
// Targets should never be shared.
|
||||||
pxAssert(!m_shared_texture);
|
pxAssert(!m_shared_texture);
|
||||||
|
|
||||||
if (m_texture)
|
if (m_texture)
|
||||||
{
|
{
|
||||||
g_texture_cache->m_target_memory_usage -= m_texture->GetMemUsage();
|
g_texture_cache->m_target_memory_usage -= m_texture->GetMemUsage();
|
||||||
|
@ -6478,7 +6734,13 @@ void GSTextureCache::Target::ResizeValidity(const GSVector4i& rect)
|
||||||
m_valid = m_valid.rintersect(rect);
|
m_valid = m_valid.rintersect(rect);
|
||||||
m_drawn_since_read = m_drawn_since_read.rintersect(rect);
|
m_drawn_since_read = m_drawn_since_read.rintersect(rect);
|
||||||
m_end_block = GSLocalMemory::GetEndBlockAddress(m_TEX0.TBP0, m_TEX0.TBW, m_TEX0.PSM, m_valid);
|
m_end_block = GSLocalMemory::GetEndBlockAddress(m_TEX0.TBP0, m_TEX0.TBW, m_TEX0.PSM, m_valid);
|
||||||
|
|
||||||
|
const u32 offset = ((UnwrappedEndBlock() + 1) - m_TEX0.TBP0) % (std::max(m_TEX0.TBW, 1U) << 5);
|
||||||
|
|
||||||
|
if (offset)
|
||||||
|
m_end_block = m_end_block + ((std::max(m_TEX0.TBW, 1U) << 5) - offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Else No valid size, so need to resize down.
|
// Else No valid size, so need to resize down.
|
||||||
|
|
||||||
// GL_CACHE("ResizeValidity (0x%x->0x%x) from R:%d,%d Valid: %d,%d", m_TEX0.TBP0, m_end_block, rect.z, rect.w, m_valid.z, m_valid.w);
|
// GL_CACHE("ResizeValidity (0x%x->0x%x) from R:%d,%d Valid: %d,%d", m_TEX0.TBP0, m_end_block, rect.z, rect.w, m_valid.z, m_valid.w);
|
||||||
|
@ -6491,17 +6753,25 @@ void GSTextureCache::Target::UpdateValidity(const GSVector4i& rect, bool can_res
|
||||||
m_valid = rect;
|
m_valid = rect;
|
||||||
|
|
||||||
m_end_block = GSLocalMemory::GetEndBlockAddress(m_TEX0.TBP0, m_TEX0.TBW, m_TEX0.PSM, m_valid);
|
m_end_block = GSLocalMemory::GetEndBlockAddress(m_TEX0.TBP0, m_TEX0.TBW, m_TEX0.PSM, m_valid);
|
||||||
|
const u32 offset = ((UnwrappedEndBlock() + 1) - m_TEX0.TBP0) % (std::max(m_TEX0.TBW, 1U) << 5);
|
||||||
|
|
||||||
|
if (offset)
|
||||||
|
m_end_block = m_end_block + ((std::max(m_TEX0.TBW, 1U) << 5) - offset);
|
||||||
}
|
}
|
||||||
else if (can_resize)
|
else if (can_resize)
|
||||||
{
|
{
|
||||||
m_valid = m_valid.runion(rect);
|
m_valid = m_valid.runion(rect);
|
||||||
|
|
||||||
m_end_block = GSLocalMemory::GetEndBlockAddress(m_TEX0.TBP0, m_TEX0.TBW, m_TEX0.PSM, m_valid);
|
m_end_block = GSLocalMemory::GetEndBlockAddress(m_TEX0.TBP0, m_TEX0.TBW, m_TEX0.PSM, m_valid);
|
||||||
|
const u32 offset = ((UnwrappedEndBlock() + 1) - m_TEX0.TBP0) % (std::max(m_TEX0.TBW, 1U) << 5);
|
||||||
|
|
||||||
|
if (offset)
|
||||||
|
m_end_block = m_end_block + ((std::max(m_TEX0.TBW, 1U) << 5) - offset);
|
||||||
}
|
}
|
||||||
// GL_CACHE("UpdateValidity (0x%x->0x%x) from R:%d,%d Valid: %d,%d", m_TEX0.TBP0, m_end_block, rect.z, rect.w, m_valid.z, m_valid.w);
|
// GL_CACHE("UpdateValidity (0x%x->0x%x) from R:%d,%d Valid: %d,%d", m_TEX0.TBP0, m_end_block, rect.z, rect.w, m_valid.z, m_valid.w);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSTextureCache::Target::ResizeTexture(int new_unscaled_width, int new_unscaled_height, bool recycle_old)
|
bool GSTextureCache::Target::ResizeTexture(int new_unscaled_width, int new_unscaled_height, bool recycle_old, bool require_new_rect, GSVector4i new_rect, bool keep_old)
|
||||||
{
|
{
|
||||||
if (m_unscaled_size.x == new_unscaled_width && m_unscaled_size.y == new_unscaled_height)
|
if (m_unscaled_size.x == new_unscaled_width && m_unscaled_size.y == new_unscaled_height)
|
||||||
return true;
|
return true;
|
||||||
|
@ -6525,7 +6795,7 @@ bool GSTextureCache::Target::ResizeTexture(int new_unscaled_width, int new_unsca
|
||||||
// Only need to copy if it's been written to.
|
// Only need to copy if it's been written to.
|
||||||
if (m_texture->GetState() == GSTexture::State::Dirty)
|
if (m_texture->GetState() == GSTexture::State::Dirty)
|
||||||
{
|
{
|
||||||
const GSVector4i rc = GSVector4i::loadh(size.min(new_size));
|
const GSVector4i rc = require_new_rect ? new_rect : GSVector4i::loadh(size.min(new_size));
|
||||||
if (tex->IsDepthStencil())
|
if (tex->IsDepthStencil())
|
||||||
{
|
{
|
||||||
// Can't do partial copies in DirectX for depth textures, and it's probably not ideal in other
|
// Can't do partial copies in DirectX for depth textures, and it's probably not ideal in other
|
||||||
|
@ -6533,10 +6803,17 @@ bool GSTextureCache::Target::ResizeTexture(int new_unscaled_width, int new_unsca
|
||||||
g_gs_device->StretchRect(m_texture, tex, GSVector4(rc), ShaderConvert::DEPTH_COPY, false);
|
g_gs_device->StretchRect(m_texture, tex, GSVector4(rc), ShaderConvert::DEPTH_COPY, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
if (require_new_rect)
|
||||||
|
{
|
||||||
|
g_gs_device->StretchRect(m_texture, tex, GSVector4(rc), ShaderConvert::COPY, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// Fast memcpy()-like path for color targets.
|
// Fast memcpy()-like path for color targets.
|
||||||
g_gs_device->CopyRect(m_texture, tex, rc, 0, 0);
|
g_gs_device->CopyRect(m_texture, tex, rc, 0, 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||||
}
|
}
|
||||||
|
@ -6553,12 +6830,18 @@ bool GSTextureCache::Target::ResizeTexture(int new_unscaled_width, int new_unsca
|
||||||
g_gs_device->InvalidateRenderTarget(tex);
|
g_gs_device->InvalidateRenderTarget(tex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!keep_old)
|
||||||
|
{
|
||||||
g_texture_cache->m_target_memory_usage = (g_texture_cache->m_target_memory_usage - m_texture->GetMemUsage()) + tex->GetMemUsage();
|
g_texture_cache->m_target_memory_usage = (g_texture_cache->m_target_memory_usage - m_texture->GetMemUsage()) + tex->GetMemUsage();
|
||||||
|
|
||||||
if (recycle_old)
|
if (recycle_old)
|
||||||
g_gs_device->Recycle(m_texture);
|
g_gs_device->Recycle(m_texture);
|
||||||
else
|
else
|
||||||
delete m_texture;
|
delete m_texture;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
g_texture_cache->m_target_memory_usage += tex->GetMemUsage();
|
||||||
|
|
||||||
m_texture = tex;
|
m_texture = tex;
|
||||||
m_unscaled_size = new_unscaled_size;
|
m_unscaled_size = new_unscaled_size;
|
||||||
|
@ -6587,8 +6870,7 @@ void GSTextureCache::SourceMap::Add(Source* s, const GIFRegTEX0& TEX0)
|
||||||
m_surfaces.insert(s);
|
m_surfaces.insert(s);
|
||||||
|
|
||||||
// The source pointer will be stored/duplicated in all m_map[array of pages]
|
// The source pointer will be stored/duplicated in all m_map[array of pages]
|
||||||
s->m_pages.loopPages([this, s](u32 page)
|
s->m_pages.loopPages([this, s](u32 page) {
|
||||||
{
|
|
||||||
s->m_erase_it[page] = m_map[page].InsertFront(s);
|
s->m_erase_it[page] = m_map[page].InsertFront(s);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6631,8 +6913,7 @@ void GSTextureCache::SourceMap::RemoveAt(Source* s)
|
||||||
GL_CACHE("TC: Remove Src Texture: 0x%x TBW %u PSM %s",
|
GL_CACHE("TC: Remove Src Texture: 0x%x TBW %u PSM %s",
|
||||||
s->m_TEX0.TBP0, s->m_TEX0.TBW, psm_str(s->m_TEX0.PSM));
|
s->m_TEX0.TBP0, s->m_TEX0.TBW, psm_str(s->m_TEX0.PSM));
|
||||||
|
|
||||||
s->m_pages.loopPages([this, s](u32 page)
|
s->m_pages.loopPages([this, s](u32 page) {
|
||||||
{
|
|
||||||
m_map[page].EraseIndex(s->m_erase_it[page]);
|
m_map[page].EraseIndex(s->m_erase_it[page]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6869,6 +7150,29 @@ void GSTextureCache::InvalidateTemporarySource()
|
||||||
m_temporary_source = nullptr;
|
m_temporary_source = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GSTextureCache::SetTemporaryZ(GSTexture* temp_z)
|
||||||
|
{
|
||||||
|
m_temporary_z = temp_z;
|
||||||
|
}
|
||||||
|
|
||||||
|
GSTexture* GSTextureCache::GetTemporaryZ()
|
||||||
|
{
|
||||||
|
if (!m_temporary_z)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return m_temporary_z;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GSTextureCache::InvalidateTemporaryZ()
|
||||||
|
{
|
||||||
|
if (!m_temporary_z)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_gs_device->Recycle(m_temporary_z);
|
||||||
|
m_temporary_z = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void GSTextureCache::InjectHashCacheTexture(const HashCacheKey& key, GSTexture* tex, const std::pair<u8, u8>& alpha_minmax)
|
void GSTextureCache::InjectHashCacheTexture(const HashCacheKey& key, GSTexture* tex, const std::pair<u8, u8>& alpha_minmax)
|
||||||
{
|
{
|
||||||
// When we insert we update memory usage. Old texture gets removed below.
|
// When we insert we update memory usage. Old texture gets removed below.
|
||||||
|
@ -6962,6 +7266,7 @@ void GSTextureCache::Palette::InitializeTexture()
|
||||||
}
|
}
|
||||||
|
|
||||||
m_tex_palette->Update(GSVector4i(0, 0, m_pal, 1), m_clut, m_pal * sizeof(m_clut[0]));
|
m_tex_palette->Update(GSVector4i(0, 0, m_pal, 1), m_clut, m_pal * sizeof(m_clut[0]));
|
||||||
|
|
||||||
g_texture_cache->m_source_memory_usage += m_tex_palette->GetMemUsage();
|
g_texture_cache->m_source_memory_usage += m_tex_palette->GetMemUsage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7109,10 +7414,7 @@ bool GSTextureCache::SurfaceOffsetKeyEqual::operator()(const GSTextureCache::Sur
|
||||||
{
|
{
|
||||||
const SurfaceOffsetKeyElem& lhs_elem = lhs.elems[i];
|
const SurfaceOffsetKeyElem& lhs_elem = lhs.elems[i];
|
||||||
const SurfaceOffsetKeyElem& rhs_elem = rhs.elems[i];
|
const SurfaceOffsetKeyElem& rhs_elem = rhs.elems[i];
|
||||||
if (lhs_elem.bp != rhs_elem.bp
|
if (lhs_elem.bp != rhs_elem.bp || lhs_elem.bw != rhs_elem.bw || lhs_elem.psm != rhs_elem.psm || !lhs_elem.rect.eq(rhs_elem.rect))
|
||||||
|| lhs_elem.bw != rhs_elem.bw
|
|
||||||
|| lhs_elem.psm != rhs_elem.psm
|
|
||||||
|| !lhs_elem.rect.eq(rhs_elem.rect))
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -257,7 +257,7 @@ public:
|
||||||
void UpdateValidChannels(u32 psm, u32 fbmsk);
|
void UpdateValidChannels(u32 psm, u32 fbmsk);
|
||||||
|
|
||||||
/// Resizes target texture, DOES NOT RESCALE.
|
/// Resizes target texture, DOES NOT RESCALE.
|
||||||
bool ResizeTexture(int new_unscaled_width, int new_unscaled_height, bool recycle_old = true);
|
bool ResizeTexture(int new_unscaled_width, int new_unscaled_height, bool recycle_old = true, bool require_offset = false, GSVector4i offset = GSVector4i::zero(), bool keep_old = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void UpdateTextureDebugName();
|
void UpdateTextureDebugName();
|
||||||
|
@ -427,6 +427,7 @@ protected:
|
||||||
std::unordered_map<SurfaceOffsetKey, SurfaceOffset, SurfaceOffsetKeyHash, SurfaceOffsetKeyEqual> m_surface_offset_cache;
|
std::unordered_map<SurfaceOffsetKey, SurfaceOffset, SurfaceOffsetKeyHash, SurfaceOffsetKeyEqual> m_surface_offset_cache;
|
||||||
|
|
||||||
Source* m_temporary_source = nullptr; // invalidated after the draw
|
Source* m_temporary_source = nullptr; // invalidated after the draw
|
||||||
|
GSTexture* m_temporary_z = nullptr; // invalidated after the draw
|
||||||
|
|
||||||
std::unique_ptr<GSDownloadTexture> m_color_download_texture;
|
std::unique_ptr<GSDownloadTexture> m_color_download_texture;
|
||||||
std::unique_ptr<GSDownloadTexture> m_uint16_download_texture;
|
std::unique_ptr<GSDownloadTexture> m_uint16_download_texture;
|
||||||
|
@ -491,7 +492,7 @@ public:
|
||||||
Target* FindTargetOverlap(Target* target, int type, int psm);
|
Target* FindTargetOverlap(Target* target, int type, int psm);
|
||||||
Target* LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type, bool used = true, u32 fbmask = 0,
|
Target* LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type, bool used = true, u32 fbmask = 0,
|
||||||
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_rgb = true, bool preserve_alpha = true,
|
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_rgb = true, bool preserve_alpha = true,
|
||||||
const GSVector4i draw_rc = GSVector4i::zero(), bool is_shuffle = false, bool possible_clear = false, bool preserve_scale = false);
|
const GSVector4i draw_rc = GSVector4i::zero(), bool is_shuffle = false, bool possible_clear = false, bool preserve_scale = false, GSTextureCache::Source* src = nullptr, int offset = -1);
|
||||||
Target* CreateTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size,float scale, int type, bool used = true, u32 fbmask = 0,
|
Target* CreateTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size,float scale, int type, bool used = true, u32 fbmask = 0,
|
||||||
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_target = true,
|
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_target = true,
|
||||||
const GSVector4i draw_rc = GSVector4i::zero(), GSTextureCache::Source* src = nullptr);
|
const GSVector4i draw_rc = GSVector4i::zero(), GSTextureCache::Source* src = nullptr);
|
||||||
|
@ -508,7 +509,7 @@ public:
|
||||||
bool HasTargetInHeightCache(u32 bp, u32 fbw, u32 psm, u32 max_age = std::numeric_limits<u32>::max(), bool move_front = true);
|
bool HasTargetInHeightCache(u32 bp, u32 fbw, u32 psm, u32 max_age = std::numeric_limits<u32>::max(), bool move_front = true);
|
||||||
bool Has32BitTarget(u32 bp);
|
bool Has32BitTarget(u32 bp);
|
||||||
|
|
||||||
void InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 write_psm = PSMCT32);
|
void InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 write_psm = PSMCT32, u32 write_bw = 1);
|
||||||
void InvalidateVideoMemType(int type, u32 bp, u32 write_psm = PSMCT32, u32 write_fbmsk = 0, bool dirty_only = false);
|
void InvalidateVideoMemType(int type, u32 bp, u32 write_psm = PSMCT32, u32 write_fbmsk = 0, bool dirty_only = false);
|
||||||
void InvalidateVideoMemSubTarget(GSTextureCache::Target* rt);
|
void InvalidateVideoMemSubTarget(GSTextureCache::Target* rt);
|
||||||
void InvalidateVideoMem(const GSOffset& off, const GSVector4i& r, bool target = true);
|
void InvalidateVideoMem(const GSOffset& off, const GSVector4i& r, bool target = true);
|
||||||
|
@ -517,7 +518,7 @@ public:
|
||||||
/// Removes any sources which point to the specified target.
|
/// Removes any sources which point to the specified target.
|
||||||
void InvalidateSourcesFromTarget(const Target* t);
|
void InvalidateSourcesFromTarget(const Target* t);
|
||||||
|
|
||||||
/// Replaces a source's texture externally. Required for some CRC hacks.
|
/// Removes any sources which point to the same address as a new target.
|
||||||
void ReplaceSourceTexture(Source* s, GSTexture* new_texture, float new_scale, const GSVector2i& new_unscaled_size,
|
void ReplaceSourceTexture(Source* s, GSTexture* new_texture, float new_scale, const GSVector2i& new_unscaled_size,
|
||||||
HashCacheEntry* hc_entry, bool new_texture_is_shared);
|
HashCacheEntry* hc_entry, bool new_texture_is_shared);
|
||||||
|
|
||||||
|
@ -551,6 +552,11 @@ public:
|
||||||
|
|
||||||
/// Invalidates a temporary source, a partial copy only created from the current RT/DS for the current draw.
|
/// Invalidates a temporary source, a partial copy only created from the current RT/DS for the current draw.
|
||||||
void InvalidateTemporarySource();
|
void InvalidateTemporarySource();
|
||||||
|
void SetTemporaryZ(GSTexture* temp_z);
|
||||||
|
GSTexture* GetTemporaryZ();
|
||||||
|
|
||||||
|
/// Invalidates a temporary Z, a partial copy only created from the current DS for the current draw when Z is not offset but RT is
|
||||||
|
void InvalidateTemporaryZ();
|
||||||
|
|
||||||
/// Injects a texture into the hash cache, by using GSTexture::Swap(), transitively applying to all sources. Ownership of tex is transferred.
|
/// Injects a texture into the hash cache, by using GSTexture::Swap(), transitively applying to all sources. Ownership of tex is transferred.
|
||||||
void InjectHashCacheTexture(const HashCacheKey& key, GSTexture* tex, const std::pair<u8, u8>& alpha_minmax);
|
void InjectHashCacheTexture(const HashCacheKey& key, GSTexture* tex, const std::pair<u8, u8>& alpha_minmax);
|
||||||
|
|
|
@ -1168,11 +1168,8 @@ struct PSMain
|
||||||
{
|
{
|
||||||
if (PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
|
if (PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
|
||||||
{
|
{
|
||||||
C.rb = C.br;
|
C.br = C.rb;
|
||||||
float g_temp = C.g;
|
C.ag = C.ga;
|
||||||
|
|
||||||
C.g = C.a;
|
|
||||||
C.a = g_temp;
|
|
||||||
}
|
}
|
||||||
else if(PS_PROCESS_BA & SHUFFLE_READ)
|
else if(PS_PROCESS_BA & SHUFFLE_READ)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue