From d285efe3b5ebd607e188f6b5f631491e5482d76d Mon Sep 17 00:00:00 2001 From: nmzik Date: Tue, 8 Sep 2026 20:40:37 +0400 Subject: [PATCH] graphics: allow absent programmable restart indices Co-authored-by: Ettore Sartori <85788613+Ekt0re@users.noreply.github.com> --- src/graphics/host_gpu/renderer/renderDraw.cpp | 80 +++++++++---------- 1 file changed, 36 insertions(+), 44 deletions(-) diff --git a/src/graphics/host_gpu/renderer/renderDraw.cpp b/src/graphics/host_gpu/renderer/renderDraw.cpp index c2afe81cf..2bc17bf28 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.cpp +++ b/src/graphics/host_gpu/renderer/renderDraw.cpp @@ -863,8 +863,8 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw, return true; } -static bool ResolvePrimitiveRestart(const CommandBuffer& buffer, vk::PrimitiveTopology topology, - uint32_t index_type_and_size) { +static bool ResolvePrimitiveRestart(const CommandBuffer& buffer, + const DrawIndexBufferSource& source) { const auto control = buffer.GetUserConfig().GetPrimitiveResetControl(); EXIT_NOT_IMPLEMENTED((control & ~0x3u) != 0); if ((control & 0x1u) == 0) { @@ -876,26 +876,30 @@ static bool ResolvePrimitiveRestart(const CommandBuffer& buffer, vk::PrimitiveTo case Prospero::PrimitiveType::kTriStrip: break; default: return false; } - if (topology != vk::PrimitiveTopology::eLineStrip && - topology != vk::PrimitiveTopology::eTriangleStrip && - topology != vk::PrimitiveTopology::eTriangleFan) { - return false; - } - uint32_t index_mask = 0; - switch (static_cast(index_type_and_size)) { - case Prospero::IndexType::kIndex8: index_mask = 0xffu; break; - case Prospero::IndexType::kIndex16: index_mask = 0xffffu; break; - case Prospero::IndexType::kIndex32: index_mask = 0xffffffffu; break; - default: EXIT("unknown index_type_and_size: %u\n", index_type_and_size); - } - - const auto reset_index = buffer.GetRegisters().GetPrimitiveResetIndex(); + const auto element_size = source.guest_element_size; + const auto index_mask = UINT32_MAX >> ((4 - element_size) * 8); + const auto reset_index = buffer.GetRegisters().GetPrimitiveResetIndex(); if ((control & 0x2u) != 0 && (reset_index & ~index_mask) != 0) { return false; } - EXIT_NOT_IMPLEMENTED((reset_index & index_mask) != index_mask); - return true; + const auto restart_index = reset_index & index_mask; + if (restart_index == index_mask) { + // Use native restart; the 8-bit path widens its marker to 0xffff. + return true; + } + + // A game can set a custom reset value without using it in the index buffer. + // Keep restart off in that case; fail if we actually find the value. + // Scan before preparing draw resources: readback can restart the command buffer. + EXIT_NOT_IMPLEMENTED(source.address == 0); + const auto* indices = reinterpret_cast(source.address); + for (uint64_t offset = 0; offset < source.size; offset += element_size) { + uint32_t index = 0; + std::memcpy(&index, indices + offset, element_size); + EXIT_NOT_IMPLEMENTED(index == restart_index); + } + return false; } bool RenderExecutor::PrepareDrawRenderState(CommandBuffer& buffer, const DrawCallInfo& draw, @@ -1269,51 +1273,39 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer, return; } - vk::IndexType index_type = vk::IndexType::eUint16; - uint64_t index_size = 0; - bool expand_index8_to_u16 = false; - const bool primitive_restart = - ResolvePrimitiveRestart(buffer, topology, args.index_type_and_size); - + DrawIndexBufferSource index_source {}; + index_source.address = reinterpret_cast(args.index_addr); switch (static_cast(args.index_type_and_size)) { case Prospero::IndexType::kIndex16: - index_type = vk::IndexType::eUint16; - index_size = 2 * static_cast(args.index_count); + index_source.type = vk::IndexType::eUint16; + index_source.guest_element_size = 2; break; case Prospero::IndexType::kIndex32: - index_type = vk::IndexType::eUint32; - index_size = 4 * static_cast(args.index_count); + index_source.type = vk::IndexType::eUint32; + index_source.guest_element_size = 4; break; - // Some games use it - need vulkan extension case Prospero::IndexType::kIndex8: - index_type = vk::IndexType::eUint16; - index_size = static_cast(args.index_count); - expand_index8_to_u16 = true; + index_source.guest_element_size = 1; break; default: EXIT("unknown index_type_and_size: %u\n", args.index_type_and_size); } + index_source.size = static_cast(args.index_count) * index_source.guest_element_size; + const bool primitive_restart = ResolvePrimitiveRestart(buffer, index_source); - const DrawCallInfo draw {"DrawIndex", CommandBufferDebugOp::DrawIndex, args.index_count, - args.instance_count, args.first_instance}; std::vector expanded_indices; - if (expand_index8_to_u16) { + if (index_source.guest_element_size == 1) { EXIT_NOT_IMPLEMENTED(args.index_addr == nullptr); const auto* src = static_cast(args.index_addr); expanded_indices.resize(args.index_count); for (uint32_t i = 0; i < args.index_count; i++) { expanded_indices[i] = primitive_restart && src[i] == 0xffu ? 0xffffu : src[i]; } + index_source.host_data = expanded_indices.data(); + index_source.size = expanded_indices.size() * sizeof(uint16_t); } - DrawIndexBufferSource index_source {}; - index_source.address = reinterpret_cast(args.index_addr); - index_source.host_data = - expanded_indices.empty() ? nullptr : static_cast(expanded_indices.data()); - index_source.size = - expanded_indices.empty() ? index_size : expanded_indices.size() * sizeof(uint16_t); - index_source.type = index_type; - index_source.guest_element_size = static_cast(index_size / args.index_count); - + const DrawCallInfo draw {"DrawIndex", CommandBufferDebugOp::DrawIndex, args.index_count, + args.instance_count, args.first_instance}; DrawRenderState state {}; if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, true, state)) {