[GPU/Debug] Add logic and cvar to convert triangle strips to lists

This commit is contained in:
Herman S.
2025-12-06 12:13:03 +09:00
parent ebc87f2af1
commit ac610bbfe7
2 changed files with 107 additions and 1 deletions
+57 -1
View File
@@ -45,6 +45,11 @@ DEFINE_bool(
"May also be useful for graphics debugging when the debugger doesn't "
"display the geometry generated by geometry shaders properly.",
"GPU");
DEFINE_bool(
force_convert_triangle_strips_to_lists, false,
"Force CPU conversion of triangle strips to triangle lists. This may help "
"diagnose rendering issues related to triangle strip handling.",
"GPU");
DEFINE_bool(
ignore_32bit_vertex_index_support, false,
"For host graphics API downlevel testing only (useful only for Qualcomm "
@@ -115,6 +120,10 @@ bool PrimitiveProcessor::InitializeCommon(
convert_quad_lists_to_triangle_lists_ =
!quad_lists_supported ||
cvars::force_convert_quad_lists_to_triangle_lists;
// Triangle strips are supported natively on all backends, but can be forced
// to convert to lists for debugging purposes.
convert_triangle_strips_to_lists_ =
cvars::force_convert_triangle_strips_to_lists;
// No override cvars as hosts are not required to support the fallback paths
// since they require different vertex shader structure (for the fallback
// HostVertexShaderTypes).
@@ -170,6 +179,13 @@ bool PrimitiveProcessor::InitializeCommon(
} else {
builtin_ib_offset_quad_lists_to_triangle_lists_ = SIZE_MAX;
}
if (convert_triangle_strips_to_lists_) {
builtin_ib_offset_triangle_strips_to_lists_ = builtin_index_buffer_size;
builtin_index_buffer_size +=
sizeof(uint16_t) * GetTriangleStripListIndexCount(UINT16_MAX);
} else {
builtin_ib_offset_triangle_strips_to_lists_ = SIZE_MAX;
}
if (builtin_index_buffer_size) {
if (!InitializeBuiltinIndexBuffer(
builtin_index_buffer_size,
@@ -228,6 +244,28 @@ bool PrimitiveProcessor::InitializeCommon(
*(triangle_list_ptr++) = quad_first_index + 3;
}
}
if (builtin_ib_offset_triangle_strips_to_lists_ != SIZE_MAX) {
// Triangle strip to triangle list conversion.
// For each triangle in the strip (starting at vertex 2), emit
// 3 indices. Winding order alternates for each triangle.
uint16_t* triangle_list_ptr =
mapping_16bit +
builtin_ib_offset_triangle_strips_to_lists_ /
sizeof(uint16_t);
for (uint32_t i = 2; i < UINT16_MAX; ++i) {
if ((i & 1) == 0) {
// Even triangle: v0, v1, v2
*(triangle_list_ptr++) = uint16_t(i - 2);
*(triangle_list_ptr++) = uint16_t(i - 1);
*(triangle_list_ptr++) = uint16_t(i);
} else {
// Odd triangle: v1, v0, v2 (swapped to maintain winding)
*(triangle_list_ptr++) = uint16_t(i - 1);
*(triangle_list_ptr++) = uint16_t(i - 2);
*(triangle_list_ptr++) = uint16_t(i);
}
}
}
})) {
ShutdownCommon();
return false;
@@ -383,9 +421,13 @@ bool PrimitiveProcessor::Process(ProcessingResult& result_out) {
case xenos::PrimitiveType::kLineList:
case xenos::PrimitiveType::kLineStrip:
case xenos::PrimitiveType::kTriangleList:
case xenos::PrimitiveType::kTriangleStrip:
// Supported natively on all backends.
break;
case xenos::PrimitiveType::kTriangleStrip:
if (convert_triangle_strips_to_lists_) {
host_primitive_type = xenos::PrimitiveType::kTriangleList;
}
break;
case xenos::PrimitiveType::kRectangleList:
if (expand_rectangle_lists_in_vs_) {
host_primitive_type = xenos::PrimitiveType::kTriangleStrip;
@@ -563,6 +605,17 @@ bool PrimitiveProcessor::Process(ProcessingResult& result_out) {
cacheable.host_index_buffer_handle =
builtin_ib_offset_quad_lists_to_triangle_lists_;
break;
case xenos::PrimitiveType::kTriangleStrip:
assert_true(host_primitive_type ==
xenos::PrimitiveType::kTriangleList);
cacheable.host_draw_vertex_count =
GetTriangleStripListIndexCount(cacheable.host_draw_vertex_count);
cacheable.index_buffer_type =
ProcessedIndexBufferType::kHostBuiltinForAuto;
assert_true(builtin_ib_offset_triangle_strips_to_lists_ != SIZE_MAX);
cacheable.host_index_buffer_handle =
builtin_ib_offset_triangle_strips_to_lists_;
break;
default:
assert_always();
return false;
@@ -701,6 +754,9 @@ bool PrimitiveProcessor::Process(ProcessingResult& result_out) {
case xenos::PrimitiveType::kQuadList:
host_index_count_getter = GetQuadListTriangleListIndexCount;
break;
case xenos::PrimitiveType::kTriangleStrip:
host_index_count_getter = GetTriangleStripListIndexCount;
break;
default:
assert_unhandled_case(guest_primitive_type);
return false;
+50
View File
@@ -170,6 +170,9 @@ class PrimitiveProcessor {
bool IsConvertingQuadListsToTriangleLists() const {
return convert_quad_lists_to_triangle_lists_;
}
bool IsConvertingTriangleStripsToLists() const {
return convert_triangle_strips_to_lists_;
}
bool IsExpandingPointSpritesInVS() const {
return expand_point_sprites_in_vs_;
}
@@ -623,6 +626,43 @@ class PrimitiveProcessor {
}
}
// Triangle strip to triangle list conversion.
// A strip with N vertices produces (N-2) triangles, each needing 3 indices.
static constexpr uint32_t GetTriangleStripListIndexCount(
uint32_t strip_index_count) {
return strip_index_count > 2 ? (strip_index_count - 2) * 3 : 0;
}
template <typename Index, typename IndexTransform>
static void TriangleStripToList(Index* dest, const Index* source,
uint32_t source_index_count,
const IndexTransform& index_transform) {
if (source_index_count <= 2) {
// To match GetTriangleStripListIndexCount.
return;
}
// For triangle strips, the winding order alternates for each triangle.
// Even triangles: v0, v1, v2
// Odd triangles: v1, v0, v2 (swapped to maintain consistent winding)
Index v0 = index_transform(source[0]);
Index v1 = index_transform(source[1]);
for (uint32_t i = 2; i < source_index_count; ++i) {
Index v2 = index_transform(source[i]);
if ((i & 1) == 0) {
// Even triangle (0, 2, 4, ...): v0, v1, v2
*(dest++) = v0;
*(dest++) = v1;
*(dest++) = v2;
} else {
// Odd triangle (1, 3, 5, ...): v1, v0, v2 to maintain winding
*(dest++) = v1;
*(dest++) = v0;
*(dest++) = v2;
}
v0 = v1;
v1 = v2;
}
}
// Pre-gathering the ranges allows for usage of the same functions for
// conversion with and without reset. In addition, this increases safety in
// weird cases - there won't be mismatch between the pre-calculation of the
@@ -685,6 +725,14 @@ class PrimitiveProcessor {
dest_write_ptr += range_it->host_index_count;
}
break;
case xenos::PrimitiveType::kTriangleStrip:
for (PrimitiveRangeIterator range_it = ranges_beginning;
range_it != ranges_end; ++range_it) {
TriangleStripToList(dest_write_ptr, source + range_it->guest_offset,
range_it->guest_index_count, index_transform);
dest_write_ptr += range_it->host_index_count;
}
break;
default:
assert_unhandled_case(source_primitive_type);
}
@@ -699,6 +747,7 @@ class PrimitiveProcessor {
bool convert_triangle_fans_to_lists_ = false;
bool convert_line_loops_to_strips_ = false;
bool convert_quad_lists_to_triangle_lists_ = false;
bool convert_triangle_strips_to_lists_ = false;
bool expand_point_sprites_in_vs_ = false;
bool expand_rectangle_lists_in_vs_ = false;
@@ -706,6 +755,7 @@ class PrimitiveProcessor {
size_t builtin_ib_offset_two_triangle_strips_ = SIZE_MAX;
size_t builtin_ib_offset_triangle_fans_to_lists_ = SIZE_MAX;
size_t builtin_ib_offset_quad_lists_to_triangle_lists_ = SIZE_MAX;
size_t builtin_ib_offset_triangle_strips_to_lists_ = SIZE_MAX;
std::deque<SinglePrimitiveRange> single_primitive_ranges_;