From 6ba25b6cc0d738da8f9b9625f24d0af555e46380 Mon Sep 17 00:00:00 2001 From: Ribbit Date: Sun, 26 Oct 2025 01:38:08 +0200 Subject: [PATCH] [vk] Correct polygon draw topology mapping for line and point modes (#2834) Co-authored-by: Ribbit Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2834 Reviewed-by: MaranBr Reviewed-by: crueter Co-authored-by: Ribbit Co-committed-by: Ribbit --- src/video_core/buffer_cache/buffer_cache.h | 76 +++++++++++++++++-- src/video_core/polygon_mode_utils.h | 46 +++++++++++ .../renderer_vulkan/fixed_pipeline_state.cpp | 3 +- .../renderer_vulkan/line_loop_utils.h | 68 +++++++++++++++++ .../renderer_vulkan/maxwell_to_vk.cpp | 44 ++--------- .../renderer_vulkan/maxwell_to_vk.h | 52 ++++++++++++- .../renderer_vulkan/vk_graphics_pipeline.cpp | 34 ++++++++- .../renderer_vulkan/vk_rasterizer.cpp | 22 +++++- 8 files changed, 296 insertions(+), 49 deletions(-) create mode 100644 src/video_core/polygon_mode_utils.h create mode 100644 src/video_core/renderer_vulkan/line_loop_utils.h diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 39c56fb33f..ddd940c6d2 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include @@ -15,6 +16,8 @@ #include "video_core/guest_memory.h" #include "video_core/host1x/gpu_device_memory_manager.h" #include "video_core/texture_cache/util.h" +#include "video_core/polygon_mode_utils.h" +#include "video_core/renderer_vulkan/line_loop_utils.h" namespace VideoCommon { @@ -353,14 +356,37 @@ void BufferCache

::UpdateComputeBuffers() { template void BufferCache

::BindHostGeometryBuffers(bool is_indexed) { + const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); if (is_indexed) { BindHostIndexBuffer(); - } else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { - const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); - if (draw_state.topology == Maxwell::PrimitiveTopology::Quads || - draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) { - runtime.BindQuadIndexBuffer(draw_state.topology, draw_state.vertex_buffer.first, - draw_state.vertex_buffer.count); + } else { + if constexpr (!P::IS_OPENGL) { + const auto polygon_mode = VideoCore::EffectivePolygonMode(maxwell3d->regs); + if (draw_state.topology == Maxwell::PrimitiveTopology::Polygon && + polygon_mode == Maxwell::PolygonMode::Line && draw_state.vertex_buffer.count > 1) { + const u32 vertex_count = draw_state.vertex_buffer.count; + const u32 generated_count = vertex_count + 1; + const bool use_u16 = vertex_count <= 0x10000; + const u32 element_size = use_u16 ? sizeof(u16) : sizeof(u32); + auto staging = runtime.UploadStagingBuffer( + static_cast(generated_count) * element_size); + std::span dst_span{staging.mapped_span.data(), + generated_count * static_cast(element_size)}; + Vulkan::LineLoop::GenerateSequentialWithClosureRaw(dst_span, element_size); + const auto synthetic_format = use_u16 ? Maxwell::IndexFormat::UnsignedShort + : Maxwell::IndexFormat::UnsignedInt; + runtime.BindIndexBuffer(draw_state.topology, synthetic_format, + draw_state.vertex_buffer.first, generated_count, + staging.buffer, static_cast(staging.offset), + generated_count * element_size); + } + } + if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { + if (draw_state.topology == Maxwell::PrimitiveTopology::Quads || + draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) { + runtime.BindQuadIndexBuffer(draw_state.topology, draw_state.vertex_buffer.first, + draw_state.vertex_buffer.count); + } } } BindHostVertexBuffers(); @@ -689,6 +715,44 @@ void BufferCache

::BindHostIndexBuffer() { const u32 offset = buffer.Offset(channel_state->index_buffer.device_addr); const u32 size = channel_state->index_buffer.size; const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); + if constexpr (!P::IS_OPENGL) { + const auto polygon_mode = VideoCore::EffectivePolygonMode(maxwell3d->regs); + const bool polygon_line = + draw_state.topology == Maxwell::PrimitiveTopology::Polygon && + polygon_mode == Maxwell::PolygonMode::Line; + if (polygon_line && draw_state.index_buffer.count > 1) { + const u32 element_size = draw_state.index_buffer.FormatSizeInBytes(); + const size_t src_bytes = static_cast(draw_state.index_buffer.count) * element_size; + const size_t total_bytes = src_bytes + element_size; + auto staging = runtime.UploadStagingBuffer(total_bytes); + std::span dst_span{staging.mapped_span.data(), total_bytes}; + std::span src_span; + if (!draw_state.inline_index_draw_indexes.empty()) { + const u8* const src = + draw_state.inline_index_draw_indexes.data() + + static_cast(draw_state.index_buffer.first) * element_size; + src_span = {src, src_bytes}; + } else if (const u8* const cpu_base = + device_memory.GetPointer(channel_state->index_buffer.device_addr)) { + const u8* const src = cpu_base + + static_cast(draw_state.index_buffer.first) * element_size; + src_span = {src, src_bytes}; + } else { + const DAddr src_addr = + channel_state->index_buffer.device_addr + + static_cast(draw_state.index_buffer.first) * element_size; + device_memory.ReadBlockUnsafe(src_addr, dst_span.data(), src_bytes); + src_span = {dst_span.data(), src_bytes}; + } + Vulkan::LineLoop::CopyWithClosureRaw(dst_span, src_span, element_size); + buffer.MarkUsage(offset, size); + runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format, + draw_state.index_buffer.first, draw_state.index_buffer.count + 1, + staging.buffer, static_cast(staging.offset), + static_cast(total_bytes)); + return; + } + } if (!draw_state.inline_index_draw_indexes.empty()) [[unlikely]] { if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) { auto upload_staging = runtime.UploadStagingBuffer(size); diff --git a/src/video_core/polygon_mode_utils.h b/src/video_core/polygon_mode_utils.h new file mode 100644 index 0000000000..c44570693e --- /dev/null +++ b/src/video_core/polygon_mode_utils.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "video_core/engines/maxwell_3d.h" + +namespace VideoCore { + +inline Tegra::Engines::Maxwell3D::Regs::PolygonMode EffectivePolygonMode( + const Tegra::Engines::Maxwell3D::Regs& regs) { + using Maxwell = Tegra::Engines::Maxwell3D::Regs; + + const bool cull_enabled = regs.gl_cull_test_enabled != 0; + const auto cull_face = regs.gl_cull_face; + const bool cull_front = cull_enabled && (cull_face == Maxwell::CullFace::Front || + cull_face == Maxwell::CullFace::FrontAndBack); + const bool cull_back = cull_enabled && (cull_face == Maxwell::CullFace::Back || + cull_face == Maxwell::CullFace::FrontAndBack); + + const bool render_front = !cull_front; + const bool render_back = !cull_back; + + const auto front_mode = regs.polygon_mode_front; + const auto back_mode = regs.polygon_mode_back; + + if (render_front && render_back && front_mode != back_mode) { + if (front_mode == Maxwell::PolygonMode::Line || back_mode == Maxwell::PolygonMode::Line) { + return Maxwell::PolygonMode::Line; + } + if (front_mode == Maxwell::PolygonMode::Point || back_mode == Maxwell::PolygonMode::Point) { + return Maxwell::PolygonMode::Point; + } + } + + if (render_front) { + return front_mode; + } + if (render_back) { + return back_mode; + } + return front_mode; +} + +} // namespace VideoCore + diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index ff2dd90aff..453a3d942b 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -14,6 +14,7 @@ #include "video_core/engines/draw_manager.h" #include "video_core/renderer_vulkan/fixed_pipeline_state.h" #include "video_core/renderer_vulkan/vk_state_tracker.h" +#include "video_core/polygon_mode_utils.h" namespace Vulkan { namespace { @@ -64,7 +65,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0); xfb_enabled.Assign(regs.transform_feedback_enabled != 0); ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); - polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); + polygon_mode.Assign(PackPolygonMode(VideoCore::EffectivePolygonMode(regs))); tessellation_primitive.Assign(static_cast(regs.tessellation.params.domain_type.Value())); tessellation_spacing.Assign(static_cast(regs.tessellation.params.spacing.Value())); tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() == diff --git a/src/video_core/renderer_vulkan/line_loop_utils.h b/src/video_core/renderer_vulkan/line_loop_utils.h new file mode 100644 index 0000000000..71f4229d11 --- /dev/null +++ b/src/video_core/renderer_vulkan/line_loop_utils.h @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "common/assert.h" +#include "common/common_types.h" + +namespace Vulkan::LineLoop { + +inline void CopyWithClosureRaw(std::span dst, std::span src, size_t element_size) { + ASSERT_MSG(dst.size() == src.size() + element_size, "Invalid line loop copy sizes"); + if (src.empty()) { + if (!dst.empty()) { + std::fill(dst.begin(), dst.end(), u8{0}); + } + return; + } + std::memcpy(dst.data(), src.data(), src.size()); + std::memcpy(dst.data() + src.size(), src.data(), element_size); +} + +inline void GenerateSequentialWithClosureRaw(std::span dst, size_t element_size, + u64 start_value = 0) { + if (dst.empty()) { + return; + } + const size_t last = dst.size() - element_size; + size_t offset = 0; + u64 value = start_value; + while (offset < last) { + std::memcpy(dst.data() + offset, &value, element_size); + offset += element_size; + ++value; + } + std::memcpy(dst.data() + offset, &start_value, element_size); +} + +template +inline void CopyWithClosure(std::span dst, std::span src) { + ASSERT_MSG(dst.size() == src.size() + 1, "Invalid destination size for line loop copy"); + if (src.empty()) { + if (!dst.empty()) { + dst.front() = {}; + } + return; + } + std::copy(src.begin(), src.end(), dst.begin()); + dst.back() = src.front(); +} + +template +inline void GenerateSequentialWithClosure(std::span dst, T start_value = {}) { + if (dst.empty()) { + return; + } + const size_t last = dst.size() - 1; + for (size_t i = 0; i < last; ++i) { + dst[i] = static_cast(start_value + static_cast(i)); + } + dst.back() = start_value; +} + +} // namespace Vulkan::LineLoop diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index a08f2f67f9..a7a878f18c 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -326,44 +329,9 @@ VkShaderStageFlagBits ShaderStage(Shader::Stage stage) { } VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device, - Maxwell::PrimitiveTopology topology) { - switch (topology) { - case Maxwell::PrimitiveTopology::Points: - return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; - case Maxwell::PrimitiveTopology::Lines: - return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; - case Maxwell::PrimitiveTopology::LineLoop: - return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - case Maxwell::PrimitiveTopology::LineStrip: - return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; - case Maxwell::PrimitiveTopology::Triangles: - return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - case Maxwell::PrimitiveTopology::TriangleStrip: - return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; - case Maxwell::PrimitiveTopology::TriangleFan: - return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; - case Maxwell::PrimitiveTopology::LinesAdjacency: - return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY; - case Maxwell::PrimitiveTopology::LineStripAdjacency: - return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY; - case Maxwell::PrimitiveTopology::TrianglesAdjacency: - return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY; - case Maxwell::PrimitiveTopology::TriangleStripAdjacency: - return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY; - case Maxwell::PrimitiveTopology::Quads: - case Maxwell::PrimitiveTopology::QuadStrip: - // TODO: Use VK_PRIMITIVE_TOPOLOGY_QUAD_LIST_EXT/VK_PRIMITIVE_TOPOLOGY_QUAD_STRIP_EXT - // whenever it releases - return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - case Maxwell::PrimitiveTopology::Patches: - return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; - case Maxwell::PrimitiveTopology::Polygon: - LOG_WARNING(Render_Vulkan, "Draw mode is Polygon with a polygon mode of lines should be a " - "single body and not a bunch of triangles."); - return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; - } - UNIMPLEMENTED_MSG("Unimplemented topology={}", topology); - return {}; + Maxwell::PrimitiveTopology topology, + Maxwell::PolygonMode polygon_mode) { + return detail::PrimitiveTopologyNoDevice(topology, polygon_mode); } VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type, diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.h b/src/video_core/renderer_vulkan/maxwell_to_vk.h index 6f65502d61..22108a3769 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.h +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,6 +18,52 @@ namespace Vulkan::MaxwellToVK { using Maxwell = Tegra::Engines::Maxwell3D::Regs; using PixelFormat = VideoCore::Surface::PixelFormat; +namespace detail { +constexpr VkPrimitiveTopology PrimitiveTopologyNoDevice(Maxwell::PrimitiveTopology topology, + Maxwell::PolygonMode polygon_mode) { + switch (topology) { + case Maxwell::PrimitiveTopology::Points: + return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; + case Maxwell::PrimitiveTopology::Lines: + return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + case Maxwell::PrimitiveTopology::LineLoop: + return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; + case Maxwell::PrimitiveTopology::LineStrip: + return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; + case Maxwell::PrimitiveTopology::Triangles: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + case Maxwell::PrimitiveTopology::TriangleStrip: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + case Maxwell::PrimitiveTopology::TriangleFan: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; + case Maxwell::PrimitiveTopology::LinesAdjacency: + return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY; + case Maxwell::PrimitiveTopology::LineStripAdjacency: + return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY; + case Maxwell::PrimitiveTopology::TrianglesAdjacency: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY; + case Maxwell::PrimitiveTopology::TriangleStripAdjacency: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY; + case Maxwell::PrimitiveTopology::Quads: + case Maxwell::PrimitiveTopology::QuadStrip: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + case Maxwell::PrimitiveTopology::Patches: + return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; + case Maxwell::PrimitiveTopology::Polygon: + switch (polygon_mode) { + case Maxwell::PolygonMode::Fill: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; + case Maxwell::PolygonMode::Line: + return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; + case Maxwell::PolygonMode::Point: + return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; + } + break; + } + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; +} +} // namespace detail + namespace Sampler { VkFilter Filter(Tegra::Texture::TextureFilter filter); @@ -46,7 +95,8 @@ struct FormatInfo { VkShaderStageFlagBits ShaderStage(Shader::Stage stage); -VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology); +VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology, + Maxwell::PolygonMode polygon_mode); VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 745389213e..13ed1b585e 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -22,6 +23,7 @@ #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_texture_cache.h" #include "video_core/renderer_vulkan/vk_update_descriptor.h" +#include "video_core/polygon_mode_utils.h" #include "video_core/shader_notify.h" #include "video_core/texture_cache/texture_cache.h" #include "video_core/vulkan_common/vulkan_device.h" @@ -614,7 +616,10 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { vertex_input_ci.pNext = &input_divisor_ci; } const bool has_tess_stages = spv_modules[1] || spv_modules[2]; - auto input_assembly_topology = MaxwellToVK::PrimitiveTopology(device, key.state.topology); + const auto polygon_mode = + FixedPipelineState::UnpackPolygonMode(key.state.polygon_mode.Value()); + auto input_assembly_topology = + MaxwellToVK::PrimitiveTopology(device, key.state.topology, polygon_mode); if (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) { if (!has_tess_stages) { LOG_WARNING(Render_Vulkan, "Patch topology used without tessellation, using points"); @@ -629,6 +634,33 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { input_assembly_topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; } } + if (key.state.topology == Maxwell::PrimitiveTopology::Polygon) { + const auto polygon_mode_name = [polygon_mode]() -> std::string_view { + switch (polygon_mode) { + case Maxwell::PolygonMode::Fill: + return "Fill"; + case Maxwell::PolygonMode::Line: + return "Line"; + case Maxwell::PolygonMode::Point: + return "Point"; + } + return "Unknown"; + }(); + const auto vk_topology_name = [input_assembly_topology]() -> std::string_view { + switch (input_assembly_topology) { + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN: + return "TriangleFan"; + case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: + return "LineStrip"; + case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: + return "PointList"; + default: + return "Unexpected"; + } + }(); + LOG_DEBUG(Render_Vulkan, "Polygon primitive in {} mode mapped to {}", polygon_mode_name, + vk_topology_name); + } const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .pNext = nullptr, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 70ca9583f9..6bad5eca0b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -37,6 +37,7 @@ #include "video_core/renderer_vulkan/vk_update_descriptor.h" #include "video_core/shader_cache.h" #include "video_core/texture_cache/texture_cache_base.h" +#include "video_core/polygon_mode_utils.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -148,7 +149,8 @@ VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u3 return scissor; } -DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, bool is_indexed) { +DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, bool is_indexed, + Maxwell::PolygonMode polygon_mode) { DrawParams params{ .base_instance = draw_state.base_instance, .num_instances = num_instances, @@ -168,6 +170,21 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, params.base_vertex = 0; params.is_indexed = true; } + const bool polygon_line = + draw_state.topology == Maxwell::PrimitiveTopology::Polygon && + polygon_mode == Maxwell::PolygonMode::Line; + if (polygon_line) { + if (params.is_indexed) { + if (draw_state.index_buffer.count > 1) { + params.num_vertices = draw_state.index_buffer.count + 1; + } + } else if (draw_state.vertex_buffer.count > 1) { + params.num_vertices = draw_state.vertex_buffer.count + 1; + params.is_indexed = true; + params.first_index = 0; + params.base_vertex = draw_state.vertex_buffer.first; + } + } return params; } } // Anonymous namespace @@ -236,7 +253,8 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { PrepareDraw(is_indexed, [this, is_indexed, instance_count] { const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); const u32 num_instances{instance_count}; - const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)}; + const auto polygon_mode = VideoCore::EffectivePolygonMode(maxwell3d->regs); + const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed, polygon_mode)}; scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { if (draw_params.is_indexed) { cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,