graphics: allow absent programmable restart indices

Co-authored-by: Ettore Sartori <85788613+Ekt0re@users.noreply.github.com>
This commit is contained in:
nmzik
2026-09-08 20:40:37 +04:00
parent a64c8d3205
commit d285efe3b5
+36 -44
View File
@@ -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<Prospero::IndexType>(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<const uint8_t*>(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<uint64_t>(args.index_addr);
switch (static_cast<Prospero::IndexType>(args.index_type_and_size)) {
case Prospero::IndexType::kIndex16:
index_type = vk::IndexType::eUint16;
index_size = 2 * static_cast<uint64_t>(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<uint64_t>(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<uint64_t>(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<uint64_t>(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<uint16_t> 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<const uint8_t*>(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<uint64_t>(args.index_addr);
index_source.host_data =
expanded_indices.empty() ? nullptr : static_cast<const void*>(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<uint32_t>(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)) {