diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 823ecdff6b..5b72d22084 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -356,11 +356,23 @@ void BufferCache

::BindHostGeometryBuffers(bool is_indexed) { if (is_indexed) { BindHostIndexBuffer(); } else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { + using Maxwell = Tegra::Engines::Maxwell3D::Regs; + const auto& regs = maxwell3d->regs; const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); + const bool allow_polygon_mode_emulation = + regs.transform_feedback_enabled == 0 && + !regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry) && + !regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation); 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); + const auto polygon_mode = allow_polygon_mode_emulation + ? regs.polygon_mode_front + : Maxwell::PolygonMode::Fill; + if (polygon_mode != Maxwell::PolygonMode::Point) { + runtime.BindQuadIndexBuffer(draw_state.topology, polygon_mode, + draw_state.vertex_buffer.first, + draw_state.vertex_buffer.count); + } } } BindHostVertexBuffers(); @@ -736,6 +748,7 @@ bool BufferCache

::IsRegionCpuModified(DAddr addr, size_t size) { template void BufferCache

::BindHostIndexBuffer() { + using Maxwell = Tegra::Engines::Maxwell3D::Regs; Buffer& buffer = slot_buffers[channel_state->index_buffer.buffer_id]; TouchBuffer(buffer, channel_state->index_buffer.buffer_id); const u32 offset = buffer.Offset(channel_state->index_buffer.device_addr); @@ -760,8 +773,35 @@ void BufferCache

::BindHostIndexBuffer() { offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes(); runtime.BindIndexBuffer(buffer, new_offset, size); } else { + const auto& regs = maxwell3d->regs; + const bool allow_polygon_mode_emulation = + regs.transform_feedback_enabled == 0 && + !regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry) && + !regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation); + auto effective_topology = draw_state.topology; + auto polygon_mode = allow_polygon_mode_emulation ? regs.polygon_mode_front + : Maxwell::PolygonMode::Fill; + if (allow_polygon_mode_emulation) { + switch (draw_state.topology) { + case Maxwell::PrimitiveTopology::Quads: + case Maxwell::PrimitiveTopology::QuadStrip: + case Maxwell::PrimitiveTopology::Polygon: + if (polygon_mode == Maxwell::PolygonMode::Point) { + effective_topology = Maxwell::PrimitiveTopology::Points; + } else if (draw_state.topology == Maxwell::PrimitiveTopology::Polygon && + polygon_mode == Maxwell::PolygonMode::Line) { + effective_topology = Maxwell::PrimitiveTopology::LineStrip; + } + break; + default: + polygon_mode = Maxwell::PolygonMode::Fill; + break; + } + } else { + polygon_mode = Maxwell::PolygonMode::Fill; + } buffer.MarkUsage(offset, size); - runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format, + runtime.BindIndexBuffer(effective_topology, polygon_mode, draw_state.index_buffer.format, draw_state.index_buffer.first, draw_state.index_buffer.count, buffer, offset, size); } diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 8a77c8296c..fb7f340bc2 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -315,8 +315,33 @@ VkShaderStageFlagBits ShaderStage(Shader::Stage stage) { return {}; } +[[nodiscard]] bool IsUnsupportedAreaTopology(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Quads: + case Maxwell::PrimitiveTopology::QuadStrip: + case Maxwell::PrimitiveTopology::Polygon: + return true; + default: + return false; + } +} + VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device, - Maxwell::PrimitiveTopology topology) { + Maxwell::PrimitiveTopology topology, + Maxwell::PolygonMode polygon_mode, + bool allow_polygon_mode_emulation) { + if (allow_polygon_mode_emulation && IsUnsupportedAreaTopology(topology)) { + switch (polygon_mode) { + case Maxwell::PolygonMode::Point: + return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; + case Maxwell::PolygonMode::Line: + return topology == Maxwell::PrimitiveTopology::Polygon + ? VK_PRIMITIVE_TOPOLOGY_LINE_STRIP + : VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + case Maxwell::PolygonMode::Fill: + break; + } + } switch (topology) { case Maxwell::PrimitiveTopology::Points: return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.h b/src/video_core/renderer_vulkan/maxwell_to_vk.h index 8e97e48c84..e6890f18c6 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.h +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -49,7 +49,10 @@ 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 = + Maxwell::PolygonMode::Fill, + bool allow_polygon_mode_emulation = false); VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size); diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 483b756dee..d0dfac3027 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -161,10 +161,11 @@ public: index_type = IndexTypeFromNumElements(device, num_indices); const u32 num_quads = GetQuadsNum(num_indices); - const u32 num_triangle_indices = num_quads * 6; + const u32 num_generated_indices = num_quads * GetIndicesPerPrimitive(); const u32 num_first_offset_copies = 4; const size_t bytes_per_index = BytesPerIndex(index_type); - const size_t size_bytes = num_triangle_indices * bytes_per_index * num_first_offset_copies; + const size_t size_bytes = + num_generated_indices * bytes_per_index * num_first_offset_copies; const VkBufferCreateInfo buffer_ci = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, @@ -189,12 +190,12 @@ public: }(); u8* staging_data = host_visible ? buffer.Mapped().data() : staging.mapped_span.data(); - const size_t quad_size = bytes_per_index * 6; + const size_t primitive_size = bytes_per_index * GetIndicesPerPrimitive(); for (u32 first = 0; first < num_first_offset_copies; ++first) { for (u32 quad = 0; quad < num_quads; ++quad) { - MakeAndUpdateIndices(staging_data, quad_size, quad, first); - staging_data += quad_size; + MakeAndUpdateIndices(staging_data, primitive_size, quad, first); + staging_data += primitive_size; } } @@ -231,13 +232,16 @@ public: const VkIndexType index_type_ = index_type; const size_t sub_first_offset = static_cast(first % 4) * GetQuadsNum(num_indices); const size_t offset = - (sub_first_offset + GetFirstOffsetQuads(first)) * 6ULL * BytesPerIndex(index_type); + (sub_first_offset + GetFirstOffsetQuads(first)) * + static_cast(GetIndicesPerPrimitive()) * BytesPerIndex(index_type); scheduler.Record([buffer_ = *buffer, index_type_, offset](vk::CommandBuffer cmdbuf) { cmdbuf.BindIndexBuffer(buffer_, offset, index_type_); }); } protected: + virtual u32 GetIndicesPerPrimitive() const = 0; + virtual u32 GetQuadsNum(u32 num_indices) const = 0; virtual u32 GetFirstOffsetQuads(u32 first) const = 0; @@ -264,6 +268,10 @@ public: ~QuadArrayIndexBuffer() = default; private: + u32 GetIndicesPerPrimitive() const override { + return 6; + } + u32 GetQuadsNum(u32 num_indices_) const override { return num_indices_ / 4; } @@ -308,6 +316,10 @@ public: ~QuadStripIndexBuffer() = default; private: + u32 GetIndicesPerPrimitive() const override { + return 6; + } + u32 GetQuadsNum(u32 num_indices_) const override { return num_indices_ >= 4 ? (num_indices_ - 2) / 2 : 0; } @@ -343,6 +355,104 @@ private: } }; +class QuadArrayLineIndexBuffer : public QuadIndexBuffer { +public: + QuadArrayLineIndexBuffer(const Device& device_, MemoryAllocator& memory_allocator_, + Scheduler& scheduler_, StagingBufferPool& staging_pool_) + : QuadIndexBuffer(device_, memory_allocator_, scheduler_, staging_pool_) {} + + ~QuadArrayLineIndexBuffer() = default; + +private: + u32 GetIndicesPerPrimitive() const override { + return 8; + } + + u32 GetQuadsNum(u32 num_indices_) const override { + return num_indices_ / 4; + } + + u32 GetFirstOffsetQuads(u32 first) const override { + return first / 4; + } + + template + static std::array MakeIndices(u32 quad, u32 first) { + std::array indices{0, 1, 1, 2, 2, 3, 3, 0}; + for (T& index : indices) { + index = static_cast(first + index + quad * 4); + } + return indices; + } + + void MakeAndUpdateIndices(u8* staging_data, size_t primitive_size, u32 quad, + u32 first) override { + switch (index_type) { + case VK_INDEX_TYPE_UINT8_EXT: + std::memcpy(staging_data, MakeIndices(quad, first).data(), primitive_size); + break; + case VK_INDEX_TYPE_UINT16: + std::memcpy(staging_data, MakeIndices(quad, first).data(), primitive_size); + break; + case VK_INDEX_TYPE_UINT32: + std::memcpy(staging_data, MakeIndices(quad, first).data(), primitive_size); + break; + default: + ASSERT(false); + break; + } + } +}; + +class QuadStripLineIndexBuffer : public QuadIndexBuffer { +public: + QuadStripLineIndexBuffer(const Device& device_, MemoryAllocator& memory_allocator_, + Scheduler& scheduler_, StagingBufferPool& staging_pool_) + : QuadIndexBuffer(device_, memory_allocator_, scheduler_, staging_pool_) {} + + ~QuadStripLineIndexBuffer() = default; + +private: + u32 GetIndicesPerPrimitive() const override { + return 8; + } + + u32 GetQuadsNum(u32 num_indices_) const override { + return num_indices_ >= 4 ? (num_indices_ - 2) / 2 : 0; + } + + u32 GetFirstOffsetQuads(u32 first) const override { + return (first / 4) * 2; + } + + template + static std::array MakeIndices(u32 quad, u32 first) { + std::array indices{0, 1, 1, 3, 3, 2, 2, 0}; + for (T& index : indices) { + index = static_cast(first + index + quad * 2); + } + return indices; + } + + void MakeAndUpdateIndices(u8* staging_data, size_t primitive_size, u32 quad, + u32 first) override { + switch (index_type) { + case VK_INDEX_TYPE_UINT8_EXT: + std::memcpy(staging_data, MakeIndices(quad, first).data(), primitive_size); + break; + case VK_INDEX_TYPE_UINT16: + std::memcpy(staging_data, MakeIndices(quad, first).data(), primitive_size); + break; + case VK_INDEX_TYPE_UINT32: + std::memcpy(staging_data, MakeIndices(quad, first).data(), primitive_size); + break; + default: + ASSERT(false); + break; + } + } +}; + BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_, StagingBufferPool& staging_pool_, GuestDescriptorQueue& guest_descriptor_queue_, @@ -366,6 +476,10 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m scheduler_, staging_pool_); quad_strip_index_buffer = std::make_shared(device_, memory_allocator_, scheduler_, staging_pool_); + quad_array_line_index_buffer = std::make_shared( + device_, memory_allocator_, scheduler_, staging_pool_); + quad_strip_line_index_buffer = std::make_shared( + device_, memory_allocator_, scheduler_, staging_pool_); } StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) { @@ -544,12 +658,24 @@ void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t si }); } -void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format, - u32 base_vertex, u32 num_indices, VkBuffer buffer, - u32 offset, [[maybe_unused]] u32 size) { +void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, PolygonMode polygon_mode, + IndexFormat index_format, u32 base_vertex, + u32 num_indices, VkBuffer buffer, u32 offset, + [[maybe_unused]] u32 size) { VkIndexType vk_index_type = MaxwellToVK::IndexFormat(index_format); VkDeviceSize vk_offset = offset; VkBuffer vk_buffer = buffer; + if (polygon_mode == PolygonMode::Line && + (topology == PrimitiveTopology::Quads || topology == PrimitiveTopology::QuadStrip)) { + if (topology == PrimitiveTopology::Quads) { + quad_array_line_index_buffer->UpdateBuffer(num_indices); + quad_array_line_index_buffer->BindBuffer(base_vertex); + } else { + quad_strip_line_index_buffer->UpdateBuffer(num_indices); + quad_strip_line_index_buffer->BindBuffer(base_vertex); + } + return; + } if (topology == PrimitiveTopology::Quads || topology == PrimitiveTopology::QuadStrip) { vk_index_type = VK_INDEX_TYPE_UINT32; std::tie(vk_buffer, vk_offset) = @@ -575,7 +701,8 @@ void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat }); } -void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count) { +void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, PolygonMode polygon_mode, + u32 first, u32 count) { if (count == 0) { ReserveNullBuffer(); scheduler.Record([this](vk::CommandBuffer cmdbuf) { @@ -584,6 +711,17 @@ void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 fir return; } + if (polygon_mode == PolygonMode::Line) { + if (topology == PrimitiveTopology::Quads) { + quad_array_line_index_buffer->UpdateBuffer(first + count); + quad_array_line_index_buffer->BindBuffer(first); + } else if (topology == PrimitiveTopology::QuadStrip) { + quad_strip_line_index_buffer->UpdateBuffer(first + count); + quad_strip_line_index_buffer->BindBuffer(first); + } + return; + } + if (topology == PrimitiveTopology::Quads) { quad_array_index_buffer->UpdateBuffer(first + count); quad_array_index_buffer->BindBuffer(first); diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 8db5ba0805..67c92fe953 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -72,12 +72,15 @@ private: class QuadArrayIndexBuffer; class QuadStripIndexBuffer; +class QuadArrayLineIndexBuffer; +class QuadStripLineIndexBuffer; class BufferCacheRuntime { friend Buffer; using PrimitiveTopology = Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology; using IndexFormat = Tegra::Engines::Maxwell3D::Regs::IndexFormat; + using PolygonMode = Tegra::Engines::Maxwell3D::Regs::PolygonMode; public: explicit BufferCacheRuntime(const Device& device_, MemoryAllocator& memory_manager_, @@ -118,10 +121,12 @@ public: void ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t size, u32 value); - void BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format, u32 num_indices, - u32 base_vertex, VkBuffer buffer, u32 offset, u32 size); + void BindIndexBuffer(PrimitiveTopology topology, PolygonMode polygon_mode, + IndexFormat index_format, u32 base_vertex, u32 num_indices, + VkBuffer buffer, u32 offset, u32 size); - void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count); + void BindQuadIndexBuffer(PrimitiveTopology topology, PolygonMode polygon_mode, u32 first, + u32 count); void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride); @@ -181,6 +186,8 @@ private: std::shared_ptr quad_array_index_buffer; std::shared_ptr quad_strip_index_buffer; + std::shared_ptr quad_array_line_index_buffer; + std::shared_ptr quad_strip_line_index_buffer; vk::Buffer null_buffer; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index f18504e592..26d666e2e9 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -662,9 +662,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { vertex_input_ci.pNext = &input_divisor_ci; } const bool has_tess_stages = spv_modules[1] || spv_modules[2]; + const bool has_geometry_stage = spv_modules[3]; const bool dynamic_topology = key.state.extended_dynamic_state != 0; const bool dynamic_primitive_restart = key.state.extended_dynamic_state_2 != 0; - auto exact_input_assembly_topology = MaxwellToVK::PrimitiveTopology(device, key.state.topology); + const auto polygon_mode = + FixedPipelineState::UnpackPolygonMode(key.state.polygon_mode); + const bool allow_polygon_mode_emulation = + !has_tess_stages && !has_geometry_stage && key.state.xfb_enabled == 0; + auto exact_input_assembly_topology = MaxwellToVK::PrimitiveTopology( + device, key.state.topology, polygon_mode, allow_polygon_mode_emulation); if (exact_input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) { if (!has_tess_stages) { LOG_WARNING(Render_Vulkan, "Patch topology used without tessellation, using points"); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 31fb538dce..109bbd6749 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -101,6 +101,9 @@ public: bool HasTessellationStages() const noexcept { return static_cast(spv_modules[1]) || static_cast(spv_modules[2]); } + bool HasGeometryStage() const noexcept { + return static_cast(spv_modules[3]); + } GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; GraphicsPipeline(GraphicsPipeline&&) noexcept = delete; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a304618088..35c2c6f0dd 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -70,6 +70,47 @@ struct DrawParams { return topology == Maxwell::PrimitiveTopology::LineLoop; } +[[nodiscard]] bool IsUnsupportedAreaTopology(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Quads: + case Maxwell::PrimitiveTopology::QuadStrip: + case Maxwell::PrimitiveTopology::Polygon: + return true; + default: + return false; + } +} + +[[nodiscard]] bool AllowPolygonModeTopologyEmulation(const Maxwell& regs, + const GraphicsPipeline* pipeline) { + if (regs.transform_feedback_enabled != 0 || pipeline == nullptr) { + return false; + } + return !pipeline->HasTessellationStages() && !pipeline->HasGeometryStage(); +} + +[[nodiscard]] bool NeedsPolygonClosure(const MaxwellDrawState& draw_state, const Maxwell& regs, + const GraphicsPipeline* pipeline) { + return AllowPolygonModeTopologyEmulation(regs, pipeline) && + draw_state.topology == Maxwell::PrimitiveTopology::Polygon && + regs.polygon_mode_front == Maxwell::PolygonMode::Line; +} + +[[nodiscard]] bool NeedsLineClosure(const MaxwellDrawState& draw_state, const Maxwell& regs, + const GraphicsPipeline* pipeline) { + return IsLineLoop(draw_state.topology) || NeedsPolygonClosure(draw_state, regs, pipeline); +} + +[[nodiscard]] Maxwell::PolygonMode EffectivePolygonMode(const MaxwellDrawState& draw_state, + const Maxwell& regs, + const GraphicsPipeline* pipeline) { + if (!AllowPolygonModeTopologyEmulation(regs, pipeline) || + !IsUnsupportedAreaTopology(draw_state.topology)) { + return Maxwell::PolygonMode::Fill; + } + return regs.polygon_mode_front; +} + [[nodiscard]] u32 PrimitiveRestartIndex(Maxwell::IndexFormat format) { switch (format) { case Maxwell::IndexFormat::UnsignedByte: @@ -293,7 +334,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, @@ -302,13 +344,28 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, .first_index = is_indexed ? draw_state.index_buffer.first : 0, .is_indexed = is_indexed, }; + if (polygon_mode == Maxwell::PolygonMode::Point) { + return params; + } // 6 triangle vertices per quad, base vertex is part of the index // See BindQuadIndexBuffer for more details if (draw_state.topology == Maxwell::PrimitiveTopology::Quads) { + if (polygon_mode == Maxwell::PolygonMode::Line) { + params.num_vertices = (params.num_vertices / 4) * 8; + params.base_vertex = 0; + params.is_indexed = true; + return params; + } params.num_vertices = (params.num_vertices / 4) * 6; params.base_vertex = 0; params.is_indexed = true; } else if (draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) { + if (polygon_mode == Maxwell::PolygonMode::Line) { + params.num_vertices = params.num_vertices >= 4 ? ((params.num_vertices - 2) / 2) * 8 : 0; + params.base_vertex = 0; + params.is_indexed = true; + return params; + } params.num_vertices = (params.num_vertices - 2) / 2 * 6; params.base_vertex = 0; params.is_indexed = true; @@ -421,9 +478,12 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { PrepareDraw(is_indexed, [this, is_indexed, instance_count] { + GraphicsPipeline* const pipeline{pipeline_cache.CurrentGraphicsPipeline()}; const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); + const auto polygon_mode = EffectivePolygonMode(draw_state, maxwell3d->regs, pipeline); const u32 num_instances{instance_count}; - const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)}; + const DrawParams draw_params{ + MakeDrawParams(draw_state, num_instances, is_indexed, polygon_mode)}; if (IsLineLoop(draw_state.topology) && maxwell3d->regs.transform_feedback_enabled == 0) { if (EmulateLineLoopDraw(draw_state, draw_params.base_instance, draw_params.num_instances, @@ -486,7 +546,8 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { is_indexed ? "vkCmdDrawIndexed" : "vkCmdDraw", params, VK_SUCCESS); } - if (IsLineLoop(draw_state.topology) && draw_params.num_vertices >= 2) { + if (NeedsLineClosure(draw_state, maxwell3d->regs, pipeline) && + draw_params.num_vertices >= 2) { if (maxwell3d->regs.transform_feedback_enabled != 0) { query_cache.CounterEnable(VideoCommon::QueryType::StreamingByteCount, false); } @@ -502,6 +563,7 @@ void RasterizerVulkan::DrawIndirect() { const auto& params = maxwell3d->draw_manager->GetIndirectParams(); buffer_cache.SetDrawIndirect(¶ms); PrepareDraw(params.is_indexed, [this, ¶ms] { + GraphicsPipeline* const pipeline{pipeline_cache.CurrentGraphicsPipeline()}; const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); if (IsLineLoop(draw_state.topology) && (maxwell3d->regs.transform_feedback_enabled == 0 || params.is_byte_count)) { @@ -544,7 +606,7 @@ void RasterizerVulkan::DrawIndirect() { static_cast(params.stride)); } }); - if (IsLineLoop(draw_state.topology)) { + if (NeedsLineClosure(draw_state, maxwell3d->regs, pipeline)) { DrawIndirectLineLoopClosures(draw_state, params); } return; @@ -570,7 +632,7 @@ void RasterizerVulkan::DrawIndirect() { log_params, VK_SUCCESS); } - if (IsLineLoop(draw_state.topology)) { + if (NeedsLineClosure(draw_state, maxwell3d->regs, pipeline)) { DrawIndirectLineLoopClosures(draw_state, params); } }); @@ -581,7 +643,9 @@ bool RasterizerVulkan::DrawLineLoopClosure(const MaxwellDrawState& draw_state, u u32 num_instances, s32 base_vertex, u32 num_vertices, u32 first_index, bool is_indexed) { - if (!IsLineLoop(draw_state.topology) || num_instances == 0 || num_vertices < 2) { + GraphicsPipeline* const pipeline{pipeline_cache.CurrentGraphicsPipeline()}; + if (!NeedsLineClosure(draw_state, maxwell3d->regs, pipeline) || num_instances == 0 || + num_vertices < 2) { return false; } @@ -754,7 +818,8 @@ bool RasterizerVulkan::EmulateIndirectLineLoopDraw( void RasterizerVulkan::DrawIndirectLineLoopClosures( const MaxwellDrawState& draw_state, const Tegra::Engines::DrawManager::IndirectParams& params) { - if (!IsLineLoop(draw_state.topology) || params.is_byte_count) { + GraphicsPipeline* const pipeline{pipeline_cache.CurrentGraphicsPipeline()}; + if (!NeedsLineClosure(draw_state, maxwell3d->regs, pipeline) || params.is_byte_count) { return; } @@ -1880,8 +1945,14 @@ void RasterizerVulkan::UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs) return; } - const auto topology = maxwell3d->draw_manager->GetDrawState().topology; - if (!IsLineRasterizationTopology(device, topology)) { + GraphicsPipeline* const pipeline = pipeline_cache.CurrentGraphicsPipeline(); + const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); + const auto effective_polygon_mode = EffectivePolygonMode(draw_state, regs, pipeline); + const auto vk_topology = MaxwellToVK::PrimitiveTopology( + device, draw_state.topology, effective_polygon_mode, + AllowPolygonModeTopologyEmulation(regs, pipeline)); + if (vk_topology != VK_PRIMITIVE_TOPOLOGY_LINE_LIST && + vk_topology != VK_PRIMITIVE_TOPOLOGY_LINE_STRIP) { return; } @@ -2006,12 +2077,16 @@ void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& reg void RasterizerVulkan::UpdatePrimitiveTopology([[maybe_unused]] Tegra::Engines::Maxwell3D::Regs& regs) { GraphicsPipeline* pipeline = pipeline_cache.CurrentGraphicsPipeline(); const auto topology = maxwell3d->draw_manager->GetDrawState().topology; - if (!state_tracker.ChangePrimitiveTopology(topology)) { - return; - } + const auto polygon_mode = + EffectivePolygonMode(maxwell3d->draw_manager->GetDrawState(), regs, pipeline); const auto vk_topology = pipeline && pipeline->HasTessellationStages() ? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST - : MaxwellToVK::PrimitiveTopology(device, topology); + : MaxwellToVK::PrimitiveTopology( + device, topology, polygon_mode, + AllowPolygonModeTopologyEmulation(regs, pipeline)); + if (!state_tracker.ChangePrimitiveTopology(static_cast(vk_topology))) { + return; + } scheduler.Record([vk_topology](vk::CommandBuffer cmdbuf) { cmdbuf.SetPrimitiveTopologyEXT(vk_topology); }); diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 610dee618e..62b53487a2 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h @@ -271,7 +271,7 @@ public: return Exchange(Dirty::LogicOp, false); } - bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) { + bool ChangePrimitiveTopology(u32 new_topology) { const bool has_changed = current_topology != new_topology; current_topology = new_topology; return has_changed; @@ -284,7 +284,7 @@ public: void InvalidateState(); private: - static constexpr auto INVALID_TOPOLOGY = static_cast(~0u); + static constexpr u32 INVALID_TOPOLOGY = ~0u; bool Exchange(std::size_t id, bool new_value) const noexcept { const bool is_dirty = (*flags)[id]; @@ -301,7 +301,7 @@ private: Tegra::Engines::Maxwell3D::DirtyState::Flags* flags; Tegra::Engines::Maxwell3D::DirtyState::Flags default_flags; Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags; - Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY; + u32 current_topology = INVALID_TOPOLOGY; bool two_sided_stencil = false; StencilProperties front{}; StencilProperties back{};