GS: Add LOD checking to HandleAutoflush().

Only enabled when the draw might use mipmapping.
This commit is contained in:
TJnotJT 2025-08-11 09:17:02 -04:00 committed by lightningterror
parent 0c70cc7e5a
commit a33ee13bb4

View File

@ -4148,8 +4148,8 @@ __forceinline void GSState::HandleAutoFlush()
// To briefly explain what's going on here, what we are checking for is draws over a texture when the source and destination are themselves.
// Because one page of the texture gets buffered in the Texture Cache (the PS2's one) if any of those pixels are overwritten, you still read the old data.
// So we need to calculate if a page boundary is being crossed for the format it is in and if the same part of the texture being written and read inside the draw.
int lod = 0;
if (IsAutoFlushDraw(prim, lod))
int tex_layer = 0;
if (IsAutoFlushDraw(prim, tex_layer))
{
int n = 1;
u32 buff[3];
@ -4184,23 +4184,33 @@ __forceinline void GSState::HandleAutoFlush()
break;
}
const bool possible_mipmap = m_context->TEX1.MXL > 0 && m_context->TEX1.MMIN >= 2 && m_context->TEX1.MMIN <= 5;
const float K = static_cast<float>(m_context->TEX1.K) / 16;
const float powL = static_cast<float>(1 << m_context->TEX1.L);
GSVector4i tex_coord;
float vert_lod = K;
// Prepare the currently processed vertex.
if (PRIM->FST)
{
tex_coord.x = m_v.U >> 4;
tex_coord.y = m_v.V >> 4;
tex_coord.x = (m_v.U >> 4) >> tex_layer;
tex_coord.y = (m_v.V >> 4) >> tex_layer;
}
else
{
const float s = std::min((m_v.ST.S / m_v.RGBAQ.Q), 1.0f);
const float t = std::min((m_v.ST.T / m_v.RGBAQ.Q), 1.0f);
tex_coord.x = static_cast<int>((1 << m_context->TEX0.TW) * s);
tex_coord.y = static_cast<int>((1 << m_context->TEX0.TH) * t);
tex_coord.x = static_cast<int>((1 << m_context->TEX0.TW) * s) >> tex_layer;
tex_coord.y = static_cast<int>((1 << m_context->TEX0.TH) * t) >> tex_layer;
if (possible_mipmap && !m_context->TEX1.LCM)
vert_lod = -std::log2(std::abs(m_v.RGBAQ.Q)) * powL + K;
}
GSVector4i tex_rect = tex_coord.xyxy();
GSVector2i lod_range = GSVector2i(static_cast<int>(std::floor(vert_lod)), static_cast<int>(std::ceil(vert_lod)));
const GSLocalMemory::psm_t tex_psm = GSLocalMemory::m_psm[m_context->TEX0.PSM];
const GSLocalMemory::psm_t frame_psm = GSLocalMemory::m_psm[m_context->FRAME.PSM];
@ -4211,24 +4221,33 @@ __forceinline void GSState::HandleAutoFlush()
if (PRIM->FST)
{
tex_coord.x = v->U >> 4;
tex_coord.y = v->V >> 4;
tex_coord.x = (v->U >> 4) >> tex_layer;
tex_coord.y = (v->V >> 4) >> tex_layer;
}
else
{
const float s = std::min((v->ST.S / v->RGBAQ.Q), 1.0f);
const float t = std::min((v->ST.T / v->RGBAQ.Q), 1.0f);
tex_coord.x = static_cast<int>(std::round((1 << m_context->TEX0.TW) * s));
tex_coord.y = static_cast<int>(std::round((1 << m_context->TEX0.TH) * t));
tex_coord.x = static_cast<int>(std::round((1 << m_context->TEX0.TW) * s)) >> tex_layer;
tex_coord.y = static_cast<int>(std::round((1 << m_context->TEX0.TH) * t)) >> tex_layer;
if (possible_mipmap && !m_context->TEX1.LCM)
vert_lod = -std::log2(std::abs(v->RGBAQ.Q)) * powL + K;
}
tex_rect.x = std::min(tex_rect.x, tex_coord.x);
tex_rect.z = std::max(tex_rect.z, tex_coord.x);
tex_rect.y = std::min(tex_rect.y, tex_coord.y);
tex_rect.w = std::max(tex_rect.w, tex_coord.y);
lod_range.x = std::min(lod_range.x, static_cast<int>(std::floor(vert_lod)));
lod_range.y = std::max(lod_range.y, static_cast<int>(std::ceil(vert_lod)));
}
// If the current prim does not use the correct mipmap layer then we don't need to flush.
if (possible_mipmap && !(lod_range.x <= tex_layer && tex_layer <= lod_range.y))
return;
// If the draw was 1 line thick, make it larger as rects are exclusive of ends.
if (tex_rect.x == tex_rect.z)
tex_rect += GSVector4i::cxpr(0, 0, 1, 0);
@ -4240,22 +4259,22 @@ __forceinline void GSState::HandleAutoFlush()
if (PRIM->FST)
{
tex_coord.x = v->U >> 4;
tex_coord.y = v->V >> 4;
tex_coord.x = (v->U >> 4) >> tex_layer;
tex_coord.y = (v->V >> 4) >> tex_layer;
}
else
{
const float s = std::min((v->ST.S / v->RGBAQ.Q), 1.0f);
const float t = std::min((v->ST.T / v->RGBAQ.Q), 1.0f);
tex_coord.x = static_cast<int>(std::round((1 << m_context->TEX0.TW) * s));
tex_coord.y = static_cast<int>(std::round((1 << m_context->TEX0.TH) * t));
tex_coord.x = static_cast<int>(std::round((1 << m_context->TEX0.TW) * s)) >> tex_layer;
tex_coord.y = static_cast<int>(std::round((1 << m_context->TEX0.TH) * t)) >> tex_layer;
}
const int clamp_minu = m_context->CLAMP.MINU;
const int clamp_maxu = m_context->CLAMP.MAXU;
const int clamp_minv = m_context->CLAMP.MINV;
const int clamp_maxv = m_context->CLAMP.MAXV;
const int clamp_minu = m_context->CLAMP.MINU >> tex_layer;
const int clamp_maxu = m_context->CLAMP.MAXU >> tex_layer;
const int clamp_minv = m_context->CLAMP.MINV >> tex_layer;
const int clamp_maxv = m_context->CLAMP.MAXV >> tex_layer;
switch (m_context->CLAMP.WMS)
{
@ -4391,7 +4410,7 @@ __forceinline void GSState::HandleAutoFlush()
const int tex_width = (m_context->TEX0.TBW * 64) / tex_psm.pgs.x;
if ((frame_width == tex_width) || ((tex_rect.w / tex_psm.pgs.y) <= 1 && frame_width >= tex_width))
{
tex_rect += GSVector4i(0, 0, tex_page_mask.z, tex_page_mask.w); // round up to the next page as we will be comparing by page.
tex_rect += GSVector4i(0, 0, tex_psm.pgs.x - 1, tex_psm.pgs.y - 1); // round up to the next page as we will be comparing by page.
//We know we've changed page, so let's set the dimension to cover the page they're in (for different pixel orders)
tex_rect &= tex_page_mask;
tex_rect = GSVector4i(tex_rect.x / tex_psm.pgs.x, tex_rect.y / tex_psm.pgs.y, tex_rect.z / tex_psm.pgs.x, tex_rect.w / tex_psm.pgs.y);
@ -4400,7 +4419,7 @@ __forceinline void GSState::HandleAutoFlush()
const int frame_page_mask_y = ~(frame_psm.pgs.y - 1);
const GSVector4i frame_page_mask = { frame_page_mask_x, frame_page_mask_y, frame_page_mask_x, frame_page_mask_y };
GSVector4i area_out = temp_draw_rect;
area_out += GSVector4i(0, 0, frame_page_mask.z, frame_page_mask.w); // round up to the next page as we will be comparing by page.
area_out += GSVector4i(0, 0, frame_psm.pgs.x - 1, frame_psm.pgs.y - 1); // round up to the next page as we will be comparing by page.
area_out &= frame_page_mask;
area_out = GSVector4i(area_out.x / frame_psm.pgs.x, area_out.y / frame_psm.pgs.y, area_out.z / frame_psm.pgs.x, area_out.w / frame_psm.pgs.y);