This commit is contained in:
refractionpcsx2 2025-01-17 17:42:29 +01:00 committed by GitHub
commit b9749f4baa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 1180 additions and 334 deletions

View File

@ -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.

View File

@ -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)
{ {

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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));

View File

@ -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

View File

@ -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 = {};

View File

@ -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,7 +4608,10 @@ 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;

View File

@ -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);

View File

@ -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)
{ {