[vulkan] Adjust polygon mode handling + topology emulation resolve for line and point with polygon mode

This commit is contained in:
CamilleLaVey 2026-03-14 04:03:30 -04:00
parent a6c610cf26
commit 83c727794e
9 changed files with 333 additions and 36 deletions

View file

@ -356,11 +356,23 @@ void BufferCache<P>::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<P>::IsRegionCpuModified(DAddr addr, size_t size) {
template <class P>
void BufferCache<P>::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<P>::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);
}

View file

@ -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;

View file

@ -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);

View file

@ -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<size_t>(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<size_t>(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 <typename T>
static std::array<T, 8> MakeIndices(u32 quad, u32 first) {
std::array<T, 8> indices{0, 1, 1, 2, 2, 3, 3, 0};
for (T& index : indices) {
index = static_cast<T>(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<u8>(quad, first).data(), primitive_size);
break;
case VK_INDEX_TYPE_UINT16:
std::memcpy(staging_data, MakeIndices<u16>(quad, first).data(), primitive_size);
break;
case VK_INDEX_TYPE_UINT32:
std::memcpy(staging_data, MakeIndices<u32>(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 <typename T>
static std::array<T, 8> MakeIndices(u32 quad, u32 first) {
std::array<T, 8> indices{0, 1, 1, 3, 3, 2, 2, 0};
for (T& index : indices) {
index = static_cast<T>(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<u8>(quad, first).data(), primitive_size);
break;
case VK_INDEX_TYPE_UINT16:
std::memcpy(staging_data, MakeIndices<u16>(quad, first).data(), primitive_size);
break;
case VK_INDEX_TYPE_UINT32:
std::memcpy(staging_data, MakeIndices<u32>(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<QuadStripIndexBuffer>(device_, memory_allocator_,
scheduler_, staging_pool_);
quad_array_line_index_buffer = std::make_shared<QuadArrayLineIndexBuffer>(
device_, memory_allocator_, scheduler_, staging_pool_);
quad_strip_line_index_buffer = std::make_shared<QuadStripLineIndexBuffer>(
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);

View file

@ -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<QuadArrayIndexBuffer> quad_array_index_buffer;
std::shared_ptr<QuadStripIndexBuffer> quad_strip_index_buffer;
std::shared_ptr<QuadArrayLineIndexBuffer> quad_array_line_index_buffer;
std::shared_ptr<QuadStripLineIndexBuffer> quad_strip_line_index_buffer;
vk::Buffer null_buffer;

View file

@ -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");

View file

@ -101,6 +101,9 @@ public:
bool HasTessellationStages() const noexcept {
return static_cast<bool>(spv_modules[1]) || static_cast<bool>(spv_modules[2]);
}
bool HasGeometryStage() const noexcept {
return static_cast<bool>(spv_modules[3]);
}
GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete;
GraphicsPipeline(GraphicsPipeline&&) noexcept = delete;

View file

@ -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(&params);
PrepareDraw(params.is_indexed, [this, &params] {
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<u32>(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<u32>(vk_topology))) {
return;
}
scheduler.Record([vk_topology](vk::CommandBuffer cmdbuf) {
cmdbuf.SetPrimitiveTopologyEXT(vk_topology);
});

View file

@ -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<Maxwell::PrimitiveTopology>(~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{};