diff --git a/pcsx2/GS/Renderers/SW/GSRendererSW.cpp b/pcsx2/GS/Renderers/SW/GSRendererSW.cpp index bd73ecde2a..4fc316e825 100644 --- a/pcsx2/GS/Renderers/SW/GSRendererSW.cpp +++ b/pcsx2/GS/Renderers/SW/GSRendererSW.cpp @@ -1231,9 +1231,10 @@ bool GSRendererSW::GetScanlineGlobalData(SharedData* data) gd.t.mask.U32[0] = 0; break; case CLAMP_REGION_CLAMP: - // REGION_CLAMP ignores the actual texture size - gd.t.min.U16[0] = gd.t.minmax.U16[0] = context->CLAMP.MINU; - gd.t.max.U16[0] = gd.t.minmax.U16[2] = context->CLAMP.MAXU; + // REGION_CLAMP ignores the actual texture size, but tw is already optimised in GetFixedTEX0Size. + // It's important we don't go off MAXU (if bigger) here as the sw renderer can attempt to draw pixels outside the triangle which can cause out of bounds issues. + gd.t.min.U16[0] = gd.t.minmax.U16[0] = std::min(context->CLAMP.MINU, tw - 1); + gd.t.max.U16[0] = gd.t.minmax.U16[2] = std::min(context->CLAMP.MAXU, tw - 1); gd.t.mask.U32[0] = 0; break; case CLAMP_REGION_REPEAT: @@ -1259,9 +1260,10 @@ bool GSRendererSW::GetScanlineGlobalData(SharedData* data) gd.t.mask.U32[2] = 0; break; case CLAMP_REGION_CLAMP: - // REGION_CLAMP ignores the actual texture size - gd.t.min.U16[4] = gd.t.minmax.U16[1] = context->CLAMP.MINV; - gd.t.max.U16[4] = gd.t.minmax.U16[3] = context->CLAMP.MAXV; // ffx anima summon scene, when the anchor appears (th = 256, maxv > 256) + // REGION_CLAMP ignores the actual texture size, but th is already optimised in GetFixedTEX0Size + // It's important we don't go off MAXV (if bigger) here as the sw renderer can attempt to draw pixels outside the triangle which can cause out of bounds issues. + gd.t.min.U16[4] = gd.t.minmax.U16[1] = std::min(context->CLAMP.MINV, th - 1); + gd.t.max.U16[4] = gd.t.minmax.U16[3] = std::min(context->CLAMP.MAXV, th - 1); // ffx anima summon scene, when the anchor appears (th = 256, maxv > 256) gd.t.mask.U32[2] = 0; break; case CLAMP_REGION_REPEAT: