diff --git a/pcsx2/GS/GSState.cpp b/pcsx2/GS/GSState.cpp index 631f5f9340..2e11c48fbe 100644 --- a/pcsx2/GS/GSState.cpp +++ b/pcsx2/GS/GSState.cpp @@ -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(m_context->TEX1.K) / 16; + const float powL = static_cast(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((1 << m_context->TEX0.TW) * s); - tex_coord.y = static_cast((1 << m_context->TEX0.TH) * t); + tex_coord.x = static_cast((1 << m_context->TEX0.TW) * s) >> tex_layer; + tex_coord.y = static_cast((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(std::floor(vert_lod)), static_cast(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(std::round((1 << m_context->TEX0.TW) * s)); - tex_coord.y = static_cast(std::round((1 << m_context->TEX0.TH) * t)); + tex_coord.x = static_cast(std::round((1 << m_context->TEX0.TW) * s)) >> tex_layer; + tex_coord.y = static_cast(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(std::floor(vert_lod))); + lod_range.y = std::max(lod_range.y, static_cast(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(std::round((1 << m_context->TEX0.TW) * s)); - tex_coord.y = static_cast(std::round((1 << m_context->TEX0.TH) * t)); + tex_coord.x = static_cast(std::round((1 << m_context->TEX0.TW) * s)) >> tex_layer; + tex_coord.y = static_cast(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);