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{};