@@ -861,7 +861,8 @@ void GSRendererHW::ConvertSpriteTextureShuffleImpl(GSTextureCache::Target* rt, G
861861 const bool bilinear = m_vt.IsRealLinear ();
862862
863863 // Copy the attributes from the provoking vertex.
864- GSVertex v_default = primclass == GS_SPRITE_CLASS ? m_vertex.buff [1 ] : m_vertex.buff [2 ];
864+ GSVertex v_default = primclass == GS_SPRITE_CLASS ? m_vertex.buff [m_index.buff [1 ]] :
865+ m_vertex.buff [m_index.buff [2 ]];
865866
866867 const auto SetTexCoords = [&](float u, float v, GSVertex& vtx_out) {
867868 if constexpr (fst)
@@ -882,7 +883,10 @@ void GSRendererHW::ConvertSpriteTextureShuffleImpl(GSTextureCache::Target* rt, G
882883 vtx_out.XYZ .Y = xyof.y + static_cast <u32 >(y * 16 .0f );
883884 };
884885
885- const auto WriteQuad = [&](const GSVector4& xy, GSVector4 uv, GSVertex*& vout, u16 *& iout) {
886+ const auto WriteQuad = [&](const GSVector4i& xyi, const GSVector4i& uvi, GSVertex*& vout, u16 *& iout) {
887+ GSVector4 xy (xyi);
888+ GSVector4 uv (uvi);
889+
886890 if (bilinear)
887891 {
888892 // Translate to texel center for bilinear.
@@ -994,6 +998,7 @@ void GSRendererHW::ConvertSpriteTextureShuffleImpl(GSTextureCache::Target* rt, G
994998
995999 if (m_texture_shuffle.real_16_bit_source )
9961000 {
1001+ GL_INS (" Real 16 bit source: no tex coord change." );
9971002 half_u = false ;
9981003 half_v = false ;
9991004 }
@@ -1002,6 +1007,7 @@ void GSRendererHW::ConvertSpriteTextureShuffleImpl(GSTextureCache::Target* rt, G
10021007 m_texture_shuffle.type == TextureShuffleType::TwoPixel ||
10031008 m_texture_shuffle.type == TextureShuffleType::HackShuffle)
10041009 {
1010+ GL_INS (" Split shuffle/TwoPixel/HackShuffle: no pos or tex coord change." );
10051011 half_x = false ;
10061012 half_y = false ;
10071013 half_u = false ;
@@ -1012,6 +1018,8 @@ void GSRendererHW::ConvertSpriteTextureShuffleImpl(GSTextureCache::Target* rt, G
10121018 // Currently, this shuffle is done with the NFS CRC hack.
10131019 // It is a split texture shuffle, but the draws are skipped with the hack instead of
10141020 // being combined in the usual way, so we need to adjust the draw rect here.
1021+ GL_INS (" GappedSwizzle (NFS Undercover): rewriting rects to use full area." );
1022+
10151023 half_x = false ;
10161024 half_y = false ;
10171025 half_u = false ;
@@ -1020,12 +1028,12 @@ void GSRendererHW::ConvertSpriteTextureShuffleImpl(GSTextureCache::Target* rt, G
10201028 m_r = rt->GetUnscaledRect ();
10211029 tex_r = tex->GetUnscaledRect ();
10221030 scissor_r = m_r;
1023-
1024- GL_INS (" GappedSwizzle (NFS Undercover): rewriting rects to use full area." );
10251031 }
10261032 else if (m_texture_shuffle.type == TextureShuffleType::Swizzle ||
10271033 m_texture_shuffle.type == TextureShuffleType::SwizzleTex32)
10281034 {
1035+ GL_INS (" Swizzle/SwizzleTex32: no tex coord change." );
1036+
10291037 if (m_cached_ctx.FRAME .FBW == rt->m_TEX0 .TBW * 2 )
10301038 {
10311039 half_y = false ;
@@ -1044,6 +1052,7 @@ void GSRendererHW::ConvertSpriteTextureShuffleImpl(GSTextureCache::Target* rt, G
10441052 // No super source of truth here, since the width can get batted around, the valid is probably our best bet.
10451053 // Dogs will reuse the Z in a different size format for a completely unrelated draw with an FBW of 2,
10461054 // then go back to using it in full width.
1055+ GL_INS (" Non-recursive draw, complex case." );
10471056
10481057 const bool tex_tbw_is_wrong =
10491058 tex->m_target &&
@@ -1131,6 +1140,7 @@ void GSRendererHW::ConvertSpriteTextureShuffleImpl(GSTextureCache::Target* rt, G
11311140 else
11321141 {
11331142 // Handle recursive draws.
1143+ GL_INS (" Recursive draw, complex case." );
11341144
11351145 const GSVector2i& frame_pgs = GSLocalMemory::m_psm[m_cached_ctx.FRAME .PSM ].pgs ;
11361146
@@ -1227,7 +1237,7 @@ void GSRendererHW::ConvertSpriteTextureShuffleImpl(GSTextureCache::Target* rt, G
12271237 GSVertex* vout = m_vertex.buff ;
12281238 u16 * iout = m_index.buff ;
12291239
1230- WriteQuad (GSVector4 ( m_r), GSVector4 ( tex_r) , vout, iout);
1240+ WriteQuad (m_r, tex_r, vout, iout);
12311241
12321242 m_index.tail = iout - m_index.buff ;
12331243 m_vertex.head = m_vertex.tail = m_vertex.next = vout - m_vertex.buff ;
@@ -1267,7 +1277,8 @@ GSVector4 GSRendererHW::RealignTargetTextureCoordinate(const GSTextureCache::Sou
12671277{
12681278 if (GSConfig.UserHacks_HalfPixelOffset <= GSHalfPixelOffset::Normal ||
12691279 GSConfig.UserHacks_HalfPixelOffset >= GSHalfPixelOffset::Native ||
1270- GetUpscaleMultiplier () == 1 .0f || m_downscale_source || tex->GetScale () == 1 .0f )
1280+ GetUpscaleMultiplier () == 1 .0f || m_downscale_source || tex->GetScale () == 1 .0f ||
1281+ m_texture_shuffle) // Do not apply HPO on texture shuffles as it already aligns the coordinates.
12711282 {
12721283 return GSVector4 (0 .0f );
12731284 }
@@ -5359,7 +5370,9 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert
53595370{
53605371 GL_PUSH (" HW: IA" );
53615372
5362- if (GSConfig.UserHacks_ForceEvenSpritePosition && !m_isPackedUV_HackFlag && m_process_texture && PRIM->FST )
5373+ // Do not force even sprite positive for texture shuffles since the coordinates are already aligned.
5374+ if (GSConfig.UserHacks_ForceEvenSpritePosition && !m_isPackedUV_HackFlag && m_process_texture && PRIM->FST &&
5375+ !m_texture_shuffle)
53635376 {
53645377 for (u32 i = 0 ; i < m_vertex.next ; i++)
53655378 m_vertex.buff [i].UV &= 0x3FEF3FEF ;
@@ -7520,7 +7533,8 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
75207533
75217534 // Can be seen with the cabin part of the ship in God of War, offsets are required when using FST.
75227535 // ST uses a normalized position so doesn't need an offset here, will break Bionicle Heroes.
7523- if (GSConfig.UserHacks_HalfPixelOffset == GSHalfPixelOffset::NativeWTexOffset)
7536+ // Do not apply HPO on texture shuffles as it already aligns the coordinates.
7537+ if (GSConfig.UserHacks_HalfPixelOffset == GSHalfPixelOffset::NativeWTexOffset && !m_texture_shuffle)
75247538 {
75257539 const u32 psm = rt ? rt->m_TEX0 .PSM : ds->m_TEX0 .PSM ;
75267540 const bool can_offset = m_r.width () > GSLocalMemory::m_psm[psm].pgs .x || m_r.height () > GSLocalMemory::m_psm[psm].pgs .y ;
0 commit comments