This commit is contained in:
TJnotJT 2025-12-15 02:28:59 -03:00 committed by GitHub
commit 86f565530a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 49 additions and 34 deletions

View File

@ -4922,46 +4922,53 @@ bool GSRendererHW::VerifyIndices()
// Fix the colors in vertices in case the API only supports "provoking first vertex" // Fix the colors in vertices in case the API only supports "provoking first vertex"
// (i.e., when using flat shading the color comes from the first vertex, unlike PS2 // (i.e., when using flat shading the color comes from the first vertex, unlike PS2
// which is "provoking last vertex"). // which is "provoking last vertex").
void GSRendererHW::HandleProvokingVertexFirst() void GSRendererHW::HandleProvokingVertexFirst(bool swap_indices)
{ {
// Early exit conditions: pxAssertRel(!g_gs_device->Features().provoking_vertex_last && !m_conf.vs.iip,
if (g_gs_device->Features().provoking_vertex_last || // device supports provoking last vertex "Should not call HandleProvokingVertexFirst() when API uses provoking vertex last or interpolating colors.");
m_conf.vs.iip || // we are doing Gouraud shading
m_vt.m_primclass == GS_POINT_CLASS || // drawing points (one vertex per primitive; color is unambiguous)
m_vt.m_primclass == GS_SPRITE_CLASS) // drawing sprites (handled by the sprites -> triangles expand shader)
return;
const int n = GSUtil::GetClassVertexCount(m_vt.m_primclass); const int n = GSUtil::GetClassVertexCount(m_vt.m_primclass);
// If all first/last vertices have the same color there is nothing to do. if (swap_indices)
bool first_eq_last = true;
for (u32 i = 0; i < m_index.tail; i += n)
{ {
if (m_vertex.buff[m_index.buff[i]].RGBAQ.U32[0] != m_vertex.buff[m_index.buff[i + n - 1]].RGBAQ.U32[0]) // Fast path: just swap the indices. Used in cases where drawing order does not matter (triangles, expanded lines).
for (u32 i = 0; i < m_index.tail; i += n)
std::swap(m_index.buff[i], m_index.buff[i + n - 1]);
}
else
{
// Slow path: de-index and swap the vertex colors. Used in cases where the drawing order matters (lines).
// If all first/last vertices have the same color there is nothing to do.
bool first_eq_last = true;
for (u32 i = 0; i < m_index.tail; i += n)
{ {
first_eq_last = false; if (m_vertex.buff[m_index.buff[i]].RGBAQ.U32[0] != m_vertex.buff[m_index.buff[i + n - 1]].RGBAQ.U32[0])
break; {
first_eq_last = false;
break;
}
} }
} if (first_eq_last)
if (first_eq_last) return;
return;
// De-index the vertices using the copy buffer // De-index the vertices using the copy buffer
while (m_vertex.maxcount < m_index.tail) while (m_vertex.maxcount < m_index.tail)
GrowVertexBuffer(); GrowVertexBuffer();
for (int i = static_cast<int>(m_index.tail) - 1; i >= 0; i--) for (int i = static_cast<int>(m_index.tail) - 1; i >= 0; i--)
{ {
m_vertex.buff_copy[i] = m_vertex.buff[m_index.buff[i]]; m_vertex.buff_copy[i] = m_vertex.buff[m_index.buff[i]];
m_index.buff[i] = static_cast<u16>(i); m_index.buff[i] = static_cast<u16>(i);
} }
std::swap(m_vertex.buff, m_vertex.buff_copy); std::swap(m_vertex.buff, m_vertex.buff_copy);
m_vertex.head = m_vertex.next = m_vertex.tail = m_index.tail; m_vertex.head = m_vertex.next = m_vertex.tail = m_index.tail;
// Put correct color in the first vertex // Put correct color in the first vertex
for (u32 i = 0; i < m_index.tail; i += n) for (u32 i = 0; i < m_index.tail; i += n)
{ {
m_vertex.buff[i].RGBAQ.U32[0] = m_vertex.buff[i + n - 1].RGBAQ.U32[0]; m_vertex.buff[i].RGBAQ.U32[0] = m_vertex.buff[i + n - 1].RGBAQ.U32[0];
m_vertex.buff[i + n - 1].RGBAQ.U32[0] = 0xff; // Make last vertex red for debugging if used improperly m_vertex.buff[i + n - 1].RGBAQ.U32[0] = 0xff; // Make last vertex red for debugging if used improperly
}
} }
} }
@ -5035,6 +5042,13 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert
ExpandLineIndices(); ExpandLineIndices();
} }
} }
if (!features.provoking_vertex_last && !m_conf.vs.iip) // Flat shaded colors and API uses provoking vertex first
{
// Expanded lines are converted to triangles where drawing order does not matter so just swap first/last indices.
const bool swap_indices = m_conf.line_expand || (m_conf.vs.expand != GSHWDrawConfig::VSExpand::None);
HandleProvokingVertexFirst(swap_indices);
}
} }
break; break;
@ -5093,6 +5107,9 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert
GSVector4::store<true>(&v[i].ST, v_st); GSVector4::store<true>(&v[i].ST, v_st);
} }
} }
if (!features.provoking_vertex_last && !m_conf.vs.iip) // Flat shaded colors and API uses provoking vertex first
HandleProvokingVertexFirst(true); // Drawing order does not matter for triangles so just swap first/last indices.
} }
break; break;
@ -8037,8 +8054,6 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
m_conf.drawarea = m_channel_shuffle ? scissor : scissor.rintersect(ComputeBoundingBox(rtsize, rtscale)); m_conf.drawarea = m_channel_shuffle ? scissor : scissor.rintersect(ComputeBoundingBox(rtsize, rtscale));
m_conf.scissor = (DATE && !DATE_BARRIER) ? m_conf.drawarea : scissor; m_conf.scissor = (DATE && !DATE_BARRIER) ? m_conf.drawarea : scissor;
HandleProvokingVertexFirst();
SetupIA(rtscale, sx, sy, m_channel_shuffle_width != 0); SetupIA(rtscale, sx, sy, m_channel_shuffle_width != 0);
if (ate_second_pass) if (ate_second_pass)

View File

@ -93,7 +93,7 @@ private:
void DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Target* ds, GSTextureCache::Source* tex, const TextureMinMaxResult& tmm); void DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Target* ds, GSTextureCache::Source* tex, const TextureMinMaxResult& tmm);
void ResetStates(); void ResetStates();
void HandleProvokingVertexFirst(); void HandleProvokingVertexFirst(bool swap_indices);
void SetupIA(float target_scale, float sx, float sy, bool req_vert_backup); void SetupIA(float target_scale, float sx, float sy, bool req_vert_backup);
void EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GSTextureCache::Source* tex); void EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GSTextureCache::Source* tex);
bool EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only, GSTextureCache::Target* rt = nullptr); bool EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only, GSTextureCache::Target* rt = nullptr);