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. // 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. // 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. // 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; int tex_layer = 0;
if (IsAutoFlushDraw(prim, lod)) if (IsAutoFlushDraw(prim, tex_layer))
{ {
int n = 1; int n = 1;
u32 buff[3]; u32 buff[3];
@ -4184,23 +4184,33 @@ __forceinline void GSState::HandleAutoFlush()
break; 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; GSVector4i tex_coord;
float vert_lod = K;
// Prepare the currently processed vertex. // Prepare the currently processed vertex.
if (PRIM->FST) if (PRIM->FST)
{ {
tex_coord.x = m_v.U >> 4; tex_coord.x = (m_v.U >> 4) >> tex_layer;
tex_coord.y = m_v.V >> 4; tex_coord.y = (m_v.V >> 4) >> tex_layer;
} }
else else
{ {
const float s = std::min((m_v.ST.S / m_v.RGBAQ.Q), 1.0f); 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); 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.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_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(); 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 tex_psm = GSLocalMemory::m_psm[m_context->TEX0.PSM];
const GSLocalMemory::psm_t frame_psm = GSLocalMemory::m_psm[m_context->FRAME.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) if (PRIM->FST)
{ {
tex_coord.x = v->U >> 4; tex_coord.x = (v->U >> 4) >> tex_layer;
tex_coord.y = v->V >> 4; tex_coord.y = (v->V >> 4) >> tex_layer;
} }
else else
{ {
const float s = std::min((v->ST.S / v->RGBAQ.Q), 1.0f); 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); 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.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_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.x = std::min(tex_rect.x, tex_coord.x);
tex_rect.z = std::max(tex_rect.z, 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.y = std::min(tex_rect.y, tex_coord.y);
tex_rect.w = std::max(tex_rect.w, 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 the draw was 1 line thick, make it larger as rects are exclusive of ends.
if (tex_rect.x == tex_rect.z) if (tex_rect.x == tex_rect.z)
tex_rect += GSVector4i::cxpr(0, 0, 1, 0); tex_rect += GSVector4i::cxpr(0, 0, 1, 0);
@ -4240,22 +4259,22 @@ __forceinline void GSState::HandleAutoFlush()
if (PRIM->FST) if (PRIM->FST)
{ {
tex_coord.x = v->U >> 4; tex_coord.x = (v->U >> 4) >> tex_layer;
tex_coord.y = v->V >> 4; tex_coord.y = (v->V >> 4) >> tex_layer;
} }
else else
{ {
const float s = std::min((v->ST.S / v->RGBAQ.Q), 1.0f); 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); 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.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_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_minu = m_context->CLAMP.MINU >> tex_layer;
const int clamp_maxu = m_context->CLAMP.MAXU; const int clamp_maxu = m_context->CLAMP.MAXU >> tex_layer;
const int clamp_minv = m_context->CLAMP.MINV; const int clamp_minv = m_context->CLAMP.MINV >> tex_layer;
const int clamp_maxv = m_context->CLAMP.MAXV; const int clamp_maxv = m_context->CLAMP.MAXV >> tex_layer;
switch (m_context->CLAMP.WMS) 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; 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)) 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) //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 &= 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); 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 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 }; 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; 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 &= 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); 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);