From 08ede10204e7e8f682bcdc9fb2ee2a3074d10e8a Mon Sep 17 00:00:00 2001 From: TJnotJT Date: Tue, 8 Jul 2025 12:11:49 -0400 Subject: [PATCH] GS: Add secondary vertex buffer for copy/modifying vertices. Currently only used in HW renderer to fix vertices for provoking-first-vertex APIs. --- pcsx2/GS/GSState.cpp | 87 +++++++++++++------------- pcsx2/GS/GSState.h | 1 + pcsx2/GS/Renderers/HW/GSRendererHW.cpp | 33 +++------- 3 files changed, 51 insertions(+), 70 deletions(-) diff --git a/pcsx2/GS/GSState.cpp b/pcsx2/GS/GSState.cpp index 7429127dde..4273d24acc 100644 --- a/pcsx2/GS/GSState.cpp +++ b/pcsx2/GS/GSState.cpp @@ -125,6 +125,8 @@ GSState::~GSState() { if (m_vertex.buff) _aligned_free(m_vertex.buff); + if (m_vertex.buff_copy) + _aligned_free(m_vertex.buff_copy); if (m_index.buff) _aligned_free(m_index.buff); if (m_draw_vertex.buff) @@ -2859,56 +2861,53 @@ void GSState::UpdateVertexKick() void GSState::GrowVertexBuffer() { const u32 maxcount = std::max(m_vertex.maxcount * 3 / 2, 10000); + const u32 old_vertex_size = sizeof(GSVertex) * m_vertex.tail; + const u32 new_vertex_size = sizeof(GSVertex) * maxcount; + const u32 old_index_size = sizeof(u16) * m_index.tail; + const u32 new_index_size = sizeof(u16) * maxcount * 6; // Worst case index list is a list of points with vs expansion, 6 indices per point - GSVertex* vertex = static_cast(_aligned_malloc(sizeof(GSVertex) * maxcount, 32)); - GSVertex* draw_vertex = static_cast(_aligned_malloc(sizeof(GSVertex) * maxcount, 32)); - // Worst case index list is a list of points with vs expansion, 6 indices per point - u16* index = static_cast(_aligned_malloc(sizeof(u16) * maxcount * 6, 32)); - u16* draw_index = static_cast(_aligned_malloc(sizeof(u16) * maxcount * 6, 32)); - - if (!vertex || !index) + // Structure describing buffers to reallocate + struct AllocDesc { - const u32 vert_byte_count = sizeof(GSVertex) * maxcount; - const u32 idx_byte_count = sizeof(u16) * maxcount * 3; + void** pbuff; + u32 old_size; + u32 new_size; + }; + std::vector alloc_desc = { + {reinterpret_cast(&m_vertex.buff), old_vertex_size, new_vertex_size}, + // discard contents of buff_copy by setting old_size = 0 + {reinterpret_cast(&m_vertex.buff_copy), 0, new_vertex_size}, + {reinterpret_cast(&m_draw_vertex.buff), old_vertex_size, new_vertex_size}, + {reinterpret_cast(&m_index.buff), old_index_size, new_index_size}, + {reinterpret_cast(&m_draw_index.buff), old_index_size, new_index_size} + }; - Console.Error("GS: failed to allocate %zu bytes for vertices and %zu for indices.", - vert_byte_count, idx_byte_count); - pxFailRel("Memory allocation failed"); + // For logging + u32 total_size = 0; + for (const auto& desc : alloc_desc) + total_size += desc.new_size; + + // Reallocate each of the needed buffers + for (const auto [pbuff, old_size, new_size] : alloc_desc) + { + void* new_buff = _aligned_malloc(new_size, 32); + if (!new_buff) + { + Console.Error("GS: failed to allocate %zu bytes for vertices and indices.", total_size); + pxFailRel("Memory allocation failed"); + } + if (*pbuff) + { + if (old_size) + { + std::memcpy(new_buff, *pbuff, old_size); + } + _aligned_free(*pbuff); + } + *pbuff = new_buff; } - if (m_vertex.buff) - { - std::memcpy(vertex, m_vertex.buff, sizeof(GSVertex) * m_vertex.tail); - - _aligned_free(m_vertex.buff); - } - - if (m_index.buff) - { - std::memcpy(index, m_index.buff, sizeof(u16) * m_index.tail); - - _aligned_free(m_index.buff); - } - - if (m_draw_vertex.buff) - { - std::memcpy(draw_vertex, m_draw_vertex.buff, sizeof(GSVertex) * m_vertex.tail); - - _aligned_free(m_draw_vertex.buff); - } - - if (m_draw_index.buff) - { - std::memcpy(draw_index, m_draw_index.buff, sizeof(u16) * m_index.tail); - - _aligned_free(m_draw_index.buff); - } - - m_draw_vertex.buff = draw_vertex; - m_draw_index.buff = draw_index; - m_vertex.buff = vertex; m_vertex.maxcount = maxcount - 3; // -3 to have some space at the end of the buffer before DrawingKick can grow it - m_index.buff = index; } bool GSState::TrianglesAreQuads(bool shuffle_check) diff --git a/pcsx2/GS/GSState.h b/pcsx2/GS/GSState.h index 2e60282c39..e9e6e3ffdf 100644 --- a/pcsx2/GS/GSState.h +++ b/pcsx2/GS/GSState.h @@ -136,6 +136,7 @@ protected: struct { GSVertex* buff; + GSVertex* buff_copy; // same size buffer to copy/modify the original buffer u32 head, tail, next, maxcount; // head: first vertex, tail: last vertex + 1, next: last indexed + 1 u32 xy_tail; GSVector4i xy[4]; diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index 1fd7c59e89..5933d06296 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -4834,34 +4834,15 @@ void GSRendererHW::HandleProvokingVertexFirst() if (first_eq_last) return; - // De-index the vertices either in place or by reallocating the vertex buffer. - if (m_vertex.next <= m_index.tail && m_index.tail <= m_vertex.maxcount) + // De-index the vertices using the copy buffer + while (m_vertex.maxcount < m_index.tail) + GrowVertexBuffer(); + for (int i = static_cast(m_index.tail) - 1; i >= 0; i--) { - // De-index in place - for (int i = static_cast(m_index.tail) - 1; i >= 0; i--) - { - // FIXME: This might not hold with a large triangle fan with gaps (since gaps are not - // yet removed)! - pxAssert(m_index.buff[i] <= i); // At any point, there can never be more vertices than indices - m_vertex.buff[i] = m_vertex.buff[m_index.buff[i]]; - m_index.buff[i] = static_cast(i); - } - } - else - { - // Reallocate the vertex buffer - m_vertex.maxcount = std::max(m_vertex.maxcount, m_index.tail); - GSVertex* vert_buff = static_cast(_aligned_malloc(sizeof(GSVertex) * m_vertex.maxcount, 32)); - - // De-index and copy the vertices - for (u32 i = 0; i < m_index.tail; i++) - { - vert_buff[i] = m_vertex.buff[m_index.buff[i]]; - m_index.buff[i] = static_cast(i); - } - std::swap(vert_buff, m_vertex.buff); - _aligned_free(vert_buff); + m_vertex.buff_copy[i] = m_vertex.buff[m_index.buff[i]]; + m_index.buff[i] = static_cast(i); } + std::swap(m_vertex.buff, m_vertex.buff_copy); m_vertex.head = m_vertex.next = m_vertex.tail = m_index.tail; // Put correct color in the first vertex