diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 3b79cef44d..a7febdaae1 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -1211,7 +1211,8 @@ void VKGSRender::end() } std::optional > index_info = std::get<4>(upload_info); - bool single_draw = rsx::method_registers.current_draw_clause.first_count_commands.size() <= 1 || rsx::method_registers.current_draw_clause.is_disjoint_primitive; + const bool is_emulated_restart = (index_info && rsx::method_registers.restart_index_enabled() && vk::emulate_primitive_restart() && rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed); + const bool single_draw = !is_emulated_restart && (rsx::method_registers.current_draw_clause.first_count_commands.size() <= 1 || rsx::method_registers.current_draw_clause.is_disjoint_primitive); if (!index_info) { @@ -1244,12 +1245,23 @@ void VKGSRender::end() } else { - u32 first_vertex = 0; - for (const auto &range : rsx::method_registers.current_draw_clause.first_count_commands) + if (!is_emulated_restart) { - const auto verts = get_index_count(rsx::method_registers.current_draw_clause.primitive, range.second); - vkCmdDrawIndexed(*m_current_command_buffer, verts, 1, 0, first_vertex, 0); - first_vertex += verts; + u32 first_vertex = 0; + for (const auto &range : rsx::method_registers.current_draw_clause.first_count_commands) + { + const auto verts = get_index_count(rsx::method_registers.current_draw_clause.primitive, range.second); + vkCmdDrawIndexed(*m_current_command_buffer, verts, 1, first_vertex, 0, 0); + first_vertex += verts; + } + } + else + { + for (const auto &range : rsx::method_registers.current_draw_clause.alternate_first_count_commands) + { + //Primitive restart splitting happens after the primitive type expansion step + vkCmdDrawIndexed(*m_current_command_buffer, range.second, 1, range.first, 0, 0); + } } } } @@ -1839,7 +1851,7 @@ bool VKGSRender::check_program_status() properties.ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; properties.ia.topology = vk::get_appropriate_topology(rsx::method_registers.current_draw_clause.primitive, unused); - if (rsx::method_registers.restart_index_enabled()) + if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed && rsx::method_registers.restart_index_enabled() && !vk::emulate_primitive_restart()) properties.ia.primitiveRestartEnable = VK_TRUE; else properties.ia.primitiveRestartEnable = VK_FALSE; diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.cpp b/rpcs3/Emu/RSX/VK/VKHelpers.cpp index f3f3956313..0008370384 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.cpp +++ b/rpcs3/Emu/RSX/VK/VKHelpers.cpp @@ -12,6 +12,7 @@ namespace vk VkSampler g_null_sampler = nullptr; bool g_cb_no_interrupt_flag = false; + bool g_drv_no_primitive_restart_flag = false; u64 g_num_processed_frames = 0; u64 g_num_total_frames = 0; @@ -257,6 +258,34 @@ namespace vk void set_current_renderer(const vk::render_device &device) { g_current_renderer = device; + +#ifdef _WIN32 + const std::array black_listed = + { + // Black list all polaris unless its proven they dont have a problem with primitive restart + "RX 580", + "RX 570", + "RX 560", + "RX 480", + "RX 470", + "RX 460", + }; + + const auto gpu_name = g_current_renderer.gpu().name(); + for (const auto& test : black_listed) + { + if (gpu_name.find(test) != std::string::npos) + { + g_drv_no_primitive_restart_flag = true; + break; + } + } +#endif + } + + bool emulate_primitive_restart() + { + return g_drv_no_primitive_restart_flag; } void change_image_layout(VkCommandBuffer cmd, VkImage image, VkImageLayout current_layout, VkImageLayout new_layout, VkImageSubresourceRange range) diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.h b/rpcs3/Emu/RSX/VK/VKHelpers.h index 8b46cc495c..0e14088f8f 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.h +++ b/rpcs3/Emu/RSX/VK/VKHelpers.h @@ -66,6 +66,8 @@ namespace vk vk::render_device *get_current_renderer(); void set_current_renderer(const vk::render_device &device); + bool emulate_primitive_restart(); + VkComponentMapping default_component_map(); VkImageSubresource default_image_subresource(); VkImageSubresourceRange get_image_subresource_range(uint32_t base_layer, uint32_t base_mip, uint32_t layer_count, uint32_t level_count, VkImageAspectFlags aspect); diff --git a/rpcs3/Emu/RSX/VK/VKVertexBuffers.cpp b/rpcs3/Emu/RSX/VK/VKVertexBuffers.cpp index 385bbe515f..16b93c529a 100644 --- a/rpcs3/Emu/RSX/VK/VKVertexBuffers.cpp +++ b/rpcs3/Emu/RSX/VK/VKVertexBuffers.cpp @@ -141,18 +141,43 @@ namespace VkDeviceSize offset_in_index_buffer = m_index_buffer_ring_info.alloc<256>(upload_size); void* buf = m_index_buffer_ring_info.map(offset_in_index_buffer, upload_size); + gsl::span dst; + std::vector tmp; + if (rsx::method_registers.restart_index_enabled() && vk::emulate_primitive_restart()) + { + tmp.resize(upload_size); + dst = tmp; + } + else + { + dst = gsl::span(static_cast(buf), upload_size); + } + /** * Upload index (and expands it if primitive type is not natively supported). */ u32 min_index, max_index; std::tie(min_index, max_index) = write_index_array_data_to_buffer( - gsl::span(static_cast(buf), index_count * type_size), + dst, command.raw_index_buffer, index_type, rsx::method_registers.current_draw_clause.primitive, rsx::method_registers.restart_index_enabled(), rsx::method_registers.restart_index(), command.ranges_to_fetch_in_index_buffer, [](auto prim) { return !vk::is_primitive_native(prim); }); + if (rsx::method_registers.restart_index_enabled() && vk::emulate_primitive_restart()) + { + //Emulate primitive restart by breaking up the draw calls + rsx::method_registers.current_draw_clause.alternate_first_count_commands.resize(0); + + if (index_type == rsx::index_array_type::u16) + rsx::split_index_list(reinterpret_cast(tmp.data()), index_count, UINT16_MAX, rsx::method_registers.current_draw_clause.alternate_first_count_commands); + else + rsx::split_index_list(reinterpret_cast(tmp.data()), index_count, UINT32_MAX, rsx::method_registers.current_draw_clause.alternate_first_count_commands); + + memcpy(buf, tmp.data(), tmp.size()); + } + m_index_buffer_ring_info.unmap(); std::optional> index_info = diff --git a/rpcs3/Emu/RSX/rsx_methods.h b/rpcs3/Emu/RSX/rsx_methods.h index 4ca0a5c1ee..41bc2284cb 100644 --- a/rpcs3/Emu/RSX/rsx_methods.h +++ b/rpcs3/Emu/RSX/rsx_methods.h @@ -38,6 +38,11 @@ namespace rsx */ std::vector > first_count_commands; + /** + * Optionally split first-count pairs for disjoint range rendering. Valid when emulating primitive restart + */ + std::vector > alternate_first_count_commands; + /** * Returns how many vertex or index will be consumed by the draw clause. */ diff --git a/rpcs3/Emu/RSX/rsx_utils.h b/rpcs3/Emu/RSX/rsx_utils.h index b13c2d3b31..f8d088c461 100644 --- a/rpcs3/Emu/RSX/rsx_utils.h +++ b/rpcs3/Emu/RSX/rsx_utils.h @@ -251,4 +251,33 @@ namespace rsx return result; } + + template + void split_index_list(T* indices, int index_count, T restart_index, std::vector>& out) + { + int last_valid_index = -1; + int last_start = -1; + + for (int i = 0; i < index_count; ++i) + { + if (indices[i] == UINT16_MAX) + { + if (last_start >= 0) + { + out.push_back(std::make_pair(last_start, i - last_start)); + last_start = -1; + } + + continue; + } + + if (last_start < 0) + last_start = i; + + last_valid_index = i; + } + + if (last_start >= 0) + out.push_back(std::make_pair(last_start, last_valid_index - last_start + 1)); + } }