diff --git a/src/xenia/gpu/primitive_processor.cc b/src/xenia/gpu/primitive_processor.cc index 7364c36d8..129b5a16a 100644 --- a/src/xenia/gpu/primitive_processor.cc +++ b/src/xenia/gpu/primitive_processor.cc @@ -296,17 +296,18 @@ bool PrimitiveProcessor::Process(ProcessingResult& result_out) { regs.Get().tess_mode; Shader::HostVertexShaderType host_vertex_shader_type; if (tessellation_enabled) { - // Currently only supporting tessellation in known cases for safety, and not - // yet converting patch strips / fans to patch lists until games using them - // are found for easier debugging when it actually happens. - // TODO(Triang3l): Conversion of patch strips / fans if found. + // Currently only supporting tessellation in known cases for safety. host_vertex_shader_type = Shader::HostVertexShaderType(-1); switch (guest_primitive_type) { case xenos::PrimitiveType::kTriangleList: + case xenos::PrimitiveType::kTriangleFan: + case xenos::PrimitiveType::kTriangleStrip: // Also supported by triangle strips and fans according to: // https://www.khronos.org/registry/OpenGL/extensions/AMD/AMD_vertex_shader_tessellator.txt - // Would need to convert those to triangle lists, but haven't seen any - // games using tessellated strips / fans so far. + // Convert strips and fans to triangle lists for tessellation. + if (guest_primitive_type != xenos::PrimitiveType::kTriangleList) { + host_primitive_type = xenos::PrimitiveType::kTriangleList; + } switch (tessellation_mode) { case xenos::TessellationMode::kDiscrete: // - 415607E1 - nets above barrels in the beginning of the first @@ -534,11 +535,34 @@ bool PrimitiveProcessor::Process(ProcessingResult& result_out) { xenos::PrimitiveType::kTriangleList); cacheable.host_draw_vertex_count = GetTriangleFanListIndexCount(cacheable.host_draw_vertex_count); - cacheable.index_buffer_type = - ProcessedIndexBufferType::kHostBuiltinForAuto; - assert_true(builtin_ib_offset_triangle_fans_to_lists_ != SIZE_MAX); - cacheable.host_index_buffer_handle = - builtin_ib_offset_triangle_fans_to_lists_; + if (builtin_ib_offset_triangle_fans_to_lists_ != SIZE_MAX) { + cacheable.index_buffer_type = + ProcessedIndexBufferType::kHostBuiltinForAuto; + cacheable.host_index_buffer_handle = + builtin_ib_offset_triangle_fans_to_lists_; + } else if (cacheable.host_draw_vertex_count) { + // The backend didn't request the builtin fan to list conversion + // at initialization, so there's no prebuilt index buffer. This is + // only reached via the tessellation path above, which forces + // host_primitive_type to kTriangleList, so build the conversion at + // runtime. + cacheable.index_buffer_type = + ProcessedIndexBufferType::kHostConverted; + auto host_indices = reinterpret_cast( + RequestHostConvertedIndexBufferForCurrentFrame( + xenos::IndexFormat::kInt16, + cacheable.host_draw_vertex_count, false, 0, + cacheable.host_index_buffer_handle)); + if (!host_indices) { + return false; + } + uint16_t* host_indices_write = host_indices; + for (uint32_t i = 2; i < guest_draw_vertex_count; ++i) { + *(host_indices_write++) = uint16_t(i - 1); + *(host_indices_write++) = uint16_t(i); + *(host_indices_write++) = 0; + } + } break; case xenos::PrimitiveType::kLineLoop: // Plus 1 element (if there's anything to draw) in the strip, still @@ -563,6 +587,40 @@ 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); + if (cacheable.host_draw_vertex_count) { + // There's no prebuilt strip to list index buffer. This is only + // reached via the tessellation path above, which forces + // host_primitive_type to kTriangleList, so build the conversion at + // runtime. + cacheable.index_buffer_type = + ProcessedIndexBufferType::kHostConverted; + auto host_indices = reinterpret_cast( + RequestHostConvertedIndexBufferForCurrentFrame( + xenos::IndexFormat::kInt16, + cacheable.host_draw_vertex_count, false, 0, + cacheable.host_index_buffer_handle)); + if (!host_indices) { + return false; + } + uint16_t* host_indices_write = host_indices; + for (uint32_t i = 2; i < guest_draw_vertex_count; ++i) { + if ((i & 1) == 0) { + *(host_indices_write++) = uint16_t(i - 2); + *(host_indices_write++) = uint16_t(i - 1); + *(host_indices_write++) = uint16_t(i); + } else { + *(host_indices_write++) = uint16_t(i - 1); + *(host_indices_write++) = uint16_t(i - 2); + *(host_indices_write++) = uint16_t(i); + } + } + } + break; default: assert_always(); return false; diff --git a/src/xenia/gpu/primitive_processor.h b/src/xenia/gpu/primitive_processor.h index d1150e638..b64451016 100644 --- a/src/xenia/gpu/primitive_processor.h +++ b/src/xenia/gpu/primitive_processor.h @@ -548,6 +548,12 @@ class PrimitiveProcessor { uint32_t fan_index_count) { return fan_index_count > 2 ? (fan_index_count - 2) * 3 : 0; } + // 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 static void TriangleFanToList(Index* dest, const Index* source, uint32_t source_index_count,